Move the resolution sort/filter related code to camera-core
- Add SupportedOutputSizesSorter to handle the sort/filter logic for the output sizes (major for ResolutionSelector)
- Add SupportedOutputSizesSorterLegacy to handle the legacy sort/filter logic
- Keep sizes larger than the mini-bounding sizes in the sorted list but have lower priority
- Make SupportedSurfaceCombination class only handle the guaranteed configurations related logic
- Re-write tests for the change (ResolutionSelector related tests will be added when ResolutionSelector v2 design is mreged)
Bug: 270825043
Test: ./gradlew bOS
Change-Id: Ie71580a73bdb8a71d91a638ce2ec4d8a21a7a6e6
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraSurfaceAdapter.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraSurfaceAdapter.kt
index bef9275..ad5975f 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraSurfaceAdapter.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraSurfaceAdapter.kt
@@ -134,8 +134,9 @@
* @param cameraId the camera id of the camera device used by the use cases
* @param existingSurfaces list of surfaces already configured and used by the camera. The
* resolutions for these surface can not change.
- * @param newUseCaseConfigs list of configurations of the use cases that will be given a
- * suggested stream specification
+ * @param newUseCaseConfigsSupportedSizeMap map of configurations of the use cases to the
+ * supported sizes list that will be given a
+ * suggested stream specification
* @return map of suggested stream specifications for given use cases
* @throws IllegalArgumentException if {@code newUseCaseConfigs} is an empty list, if
* there isn't a supported combination of surfaces
@@ -146,7 +147,7 @@
isConcurrentCameraModeOn: Boolean,
cameraId: String,
existingSurfaces: List<AttachedSurfaceInfo>,
- newUseCaseConfigs: List<UseCaseConfig<*>>
+ newUseCaseConfigsSupportedSizeMap: Map<UseCaseConfig<*>, List<Size>>
): Map<UseCaseConfig<*>, StreamSpec> {
if (!checkIfSupportedCombinationExist(cameraId)) {
@@ -158,7 +159,7 @@
return supportedSurfaceCombinationMap[cameraId]!!.getSuggestedStreamSpecifications(
isConcurrentCameraModeOn,
existingSurfaces,
- newUseCaseConfigs
+ newUseCaseConfigsSupportedSizeMap
)
}
}
\ No newline at end of file
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/SupportedSurfaceCombination.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/SupportedSurfaceCombination.kt
index 2c23b2f..6733cc7 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/SupportedSurfaceCombination.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/SupportedSurfaceCombination.kt
@@ -26,33 +26,22 @@
import android.media.CamcorderProfile
import android.media.MediaRecorder
import android.os.Build
-import android.util.Rational
import android.util.Size
import android.view.Display
-import android.view.Surface
import androidx.annotation.RequiresApi
import androidx.camera.camera2.pipe.CameraMetadata
-import androidx.camera.core.AspectRatio
-import androidx.camera.core.Logger
import androidx.camera.core.impl.AttachedSurfaceInfo
import androidx.camera.core.impl.EncoderProfilesProxy
import androidx.camera.core.impl.ImageFormatConstants
-import androidx.camera.core.impl.ImageOutputConfig
import androidx.camera.core.impl.StreamSpec
import androidx.camera.core.impl.SurfaceCombination
import androidx.camera.core.impl.SurfaceConfig
import androidx.camera.core.impl.SurfaceSizeDefinition
import androidx.camera.core.impl.UseCaseConfig
-import androidx.camera.core.impl.utils.AspectRatioUtil
-import androidx.camera.core.impl.utils.AspectRatioUtil.CompareAspectRatiosByMappingAreaInFullFovAspectRatioSpace
-import androidx.camera.core.impl.utils.AspectRatioUtil.hasMatchingAspectRatio
-import androidx.camera.core.impl.utils.CameraOrientationUtil
import androidx.camera.core.impl.utils.CompareSizesByArea
-import androidx.camera.core.internal.utils.SizeUtil
import androidx.camera.core.internal.utils.SizeUtil.RESOLUTION_1080P
import androidx.camera.core.internal.utils.SizeUtil.RESOLUTION_480P
import androidx.camera.core.internal.utils.SizeUtil.RESOLUTION_VGA
-import androidx.core.util.Preconditions
import java.util.Arrays
import java.util.Collections
@@ -77,7 +66,6 @@
private val hardwareLevel =
cameraMetadata[CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL]
?: CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY
- private val isSensorLandscapeResolution = isSensorLandscapeResolution(cameraMetadata)
private val concurrentSurfaceCombinations: MutableList<SurfaceCombination> = ArrayList()
private val surfaceCombinations: MutableList<SurfaceCombination> = ArrayList()
private val outputSizesCache: MutableMap<Int, Array<Size>> = HashMap()
@@ -86,8 +74,6 @@
internal lateinit var surfaceSizeDefinition: SurfaceSizeDefinition
private val displayManager: DisplayManager =
(context.getSystemService(Context.DISPLAY_SERVICE) as DisplayManager)
- private val activeArraySize =
- cameraMetadata[CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE]
init {
checkCapabilities()
@@ -145,7 +131,8 @@
*
* @param isConcurrentCameraModeOn true if concurrent camera mode is on, otherwise false.
* @param existingSurfaces the existing surfaces.
- * @param newUseCaseConfigs newly added UseCaseConfig.
+ * @param newUseCaseConfigsSupportedSizeMap newly added UseCaseConfig to supported output sizes
+ * map.
* @return the suggested stream specs, which is a mapping from UseCaseConfig to the suggested
* stream specification.
* @throws IllegalArgumentException if the suggested solution for newUseCaseConfigs cannot be
@@ -154,13 +141,14 @@
fun getSuggestedStreamSpecifications(
isConcurrentCameraModeOn: Boolean,
existingSurfaces: List<AttachedSurfaceInfo>,
- newUseCaseConfigs: List<UseCaseConfig<*>>
+ newUseCaseConfigsSupportedSizeMap: Map<UseCaseConfig<*>, List<Size>>
): Map<UseCaseConfig<*>, StreamSpec> {
refreshPreviewSize()
val surfaceConfigs: MutableList<SurfaceConfig> = ArrayList()
for (scc in existingSurfaces) {
surfaceConfigs.add(scc.surfaceConfig)
}
+ val newUseCaseConfigs = newUseCaseConfigsSupportedSizeMap.keys.toList()
// Use the small size (640x480) for new use cases to check whether there is any possible
// supported combination first
for (useCaseConfig in newUseCaseConfigs) {
@@ -192,9 +180,8 @@
// Collect supported output sizes for all use cases
for (index in useCasesPriorityOrder) {
- val supportedOutputSizes: List<Size> = getSupportedOutputSizes(
- newUseCaseConfigs[index]
- )
+ val supportedOutputSizes: List<Size> =
+ newUseCaseConfigsSupportedSizeMap[newUseCaseConfigs[index]]!!
supportedOutputSizesList.add(supportedOutputSizes)
}
// Get all possible size arrangements
@@ -405,17 +392,6 @@
}
/**
- * Check if the size obtained from sensor info indicates landscape mode.
- */
- private fun isSensorLandscapeResolution(cameraMetadata: CameraMetadata): Boolean {
- val pixelArraySize: Size? =
- cameraMetadata.get<Size>(CameraCharacteristics.SENSOR_INFO_PIXEL_ARRAY_SIZE)
-
- // Make the default value is true since usually the sensor resolution is landscape.
- return if (pixelArraySize != null) pixelArraySize.width >= pixelArraySize.height else true
- }
-
- /**
* Calculates the size for preview. If the max size is larger than 1080p, use 1080p.
*/
@SuppressWarnings("deprecation")
@@ -546,389 +522,6 @@
}
/**
- * Retrieves the sorted customized supported resolutions from the given config
- */
- private fun getCustomizedSupportSizesFromConfig(
- imageFormat: Int,
- config: ImageOutputConfig
- ): Array<Size>? {
- var outputSizes: Array<Size>? = null
-
- // Try to retrieve customized supported resolutions from config.
- val formatResolutionsPairList = config.getSupportedResolutions(null)
- if (formatResolutionsPairList != null) {
- for (formatResolutionPair in formatResolutionsPairList) {
- if (formatResolutionPair.first == imageFormat) {
- outputSizes = formatResolutionPair.second
- break
- }
- }
- }
- if (outputSizes != null) {
- // TODO(b/244477758): Exclude problematic sizes
-
- // Sort the output sizes. The Comparator result must be reversed to have a descending
- // order result.
- Arrays.sort(outputSizes, CompareSizesByArea(true))
- }
- return outputSizes
- }
-
- /**
- * Flips the size if rotation is needed.
- */
- private fun flipSizeByRotation(size: Size?, targetRotation: Int): Size? {
- var outputSize = size
- // Calibrates the size with the display and sensor rotation degrees values.
- if (size != null && isRotationNeeded(targetRotation)) {
- outputSize = Size(/* width= */size.height, /* height= */size.width)
- }
- return outputSize
- }
-
- /**
- * Determines whether rotation needs to be done on target rotation.
- */
- private fun isRotationNeeded(targetRotation: Int): Boolean {
- val sensorOrientation: Int? =
- cameraMetadata[CameraCharacteristics.SENSOR_ORIENTATION]
- Preconditions.checkNotNull(
- sensorOrientation, "Camera HAL in bad state, unable to " +
- "retrieve the SENSOR_ORIENTATION"
- )
- val relativeRotationDegrees = CameraOrientationUtil.surfaceRotationToDegrees(targetRotation)
-
- // Currently this assumes that a back-facing camera is always opposite to the screen.
- // This may not be the case for all devices, so in the future we may need to handle that
- // scenario.
- val lensFacing: Int? = cameraMetadata[CameraCharacteristics.LENS_FACING]
- Preconditions.checkNotNull(
- lensFacing, "Camera HAL in bad state, unable to retrieve the " +
- "LENS_FACING"
- )
- val isOppositeFacingScreen = CameraCharacteristics.LENS_FACING_BACK == lensFacing
- val sensorRotationDegrees = CameraOrientationUtil.getRelativeImageRotation(
- relativeRotationDegrees,
- sensorOrientation!!,
- isOppositeFacingScreen
- )
- return sensorRotationDegrees == 90 || sensorRotationDegrees == 270
- }
-
- /**
- * Obtains the target size from ImageOutputConfig.
- */
- private fun getTargetSize(imageOutputConfig: ImageOutputConfig): Size? {
- val targetRotation = imageOutputConfig.getTargetRotation(Surface.ROTATION_0)
- // Calibrate targetSize by the target rotation value.
- var targetSize = imageOutputConfig.getTargetResolution(null)
- targetSize = flipSizeByRotation(targetSize, targetRotation)
- return targetSize
- }
-
- /**
- * Returns the aspect ratio group key of the target size when grouping the input resolution
- * candidate list.
- *
- * The resolution candidate list will be grouped with mod 16 consideration. Therefore, we
- * also need to consider the mod 16 factor to find which aspect ratio of group the target size
- * might be put in. So that sizes of the group will be selected to use in the highest priority.
- */
- private fun getAspectRatioGroupKeyOfTargetSize(
- targetSize: Size?,
- resolutionCandidateList: List<Size>
- ): Rational? {
- if (targetSize == null) {
- return null
- }
-
- val aspectRatios = getResolutionListGroupingAspectRatioKeys(
- resolutionCandidateList
- )
- aspectRatios.forEach {
- if (hasMatchingAspectRatio(targetSize, it)) {
- return it
- }
- }
- return Rational(targetSize.width, targetSize.height)
- }
-
- /**
- * Returns the grouping aspect ratio keys of the input resolution list.
- *
- * Some sizes might be mod16 case. When grouping, those sizes will be grouped into an
- * existing aspect ratio group if the aspect ratio can match by the mod16 rule.
- */
- private fun getResolutionListGroupingAspectRatioKeys(
- resolutionCandidateList: List<Size>
- ): List<Rational> {
- val aspectRatios: MutableList<Rational> = mutableListOf()
-
- // Adds the default 4:3 and 16:9 items first to avoid their mod16 sizes to create
- // additional items.
- aspectRatios.add(AspectRatioUtil.ASPECT_RATIO_4_3)
- aspectRatios.add(AspectRatioUtil.ASPECT_RATIO_16_9)
-
- // Tries to find the aspect ratio which the target size belongs to.
- resolutionCandidateList.forEach { size ->
- val newRatio = Rational(size.width, size.height)
- var aspectRatioFound = aspectRatios.contains(newRatio)
-
- // The checking size might be a mod16 size which can be mapped to an existing aspect
- // ratio group.
- if (!aspectRatioFound) {
- var hasMatchingAspectRatio = false
- aspectRatios.forEach loop@{ aspectRatio ->
- if (hasMatchingAspectRatio(size, aspectRatio)) {
- hasMatchingAspectRatio = true
- return@loop
- }
- }
- if (!hasMatchingAspectRatio) {
- aspectRatios.add(newRatio)
- }
- }
- }
- return aspectRatios
- }
-
- /**
- * Returns the target aspect ratio value corrected by quirks.
- *
- * The final aspect ratio is determined by the following order:
- * 1. The aspect ratio returned by TargetAspectRatio quirk (not implemented yet).
- * 2. The use case's original aspect ratio if TargetAspectRatio quirk returns RATIO_ORIGINAL
- * and the use case has target aspect ratio setting.
- * 3. The aspect ratio of use case's target size setting if TargetAspectRatio quirk returns
- * RATIO_ORIGINAL and the use case has no target aspect ratio but has target size setting.
- *
- * @param imageOutputConfig the image output config of the use case.
- * @param resolutionCandidateList the resolution candidate list which will be used to
- * determine the aspect ratio by target size when target
- * aspect ratio setting is not set.
- */
- private fun getTargetAspectRatio(
- imageOutputConfig: ImageOutputConfig,
- resolutionCandidateList: List<Size>
- ): Rational? {
- var outputRatio: Rational? = null
- // TODO(b/245622117) Get the corrected aspect ratio from quirks instead of always using
- // TargetAspectRatio.RATIO_ORIGINAL
- if (imageOutputConfig.hasTargetAspectRatio()) {
- when (@AspectRatio.Ratio val aspectRatio = imageOutputConfig.targetAspectRatio) {
- AspectRatio.RATIO_4_3 -> outputRatio =
- if (isSensorLandscapeResolution) AspectRatioUtil.ASPECT_RATIO_4_3
- else AspectRatioUtil.ASPECT_RATIO_3_4
- AspectRatio.RATIO_16_9 -> outputRatio =
- if (isSensorLandscapeResolution) AspectRatioUtil.ASPECT_RATIO_16_9
- else AspectRatioUtil.ASPECT_RATIO_9_16
- AspectRatio.RATIO_DEFAULT -> Unit
- else -> Logger.e(
- TAG,
- "Undefined target aspect ratio: $aspectRatio"
- )
- }
- } else {
- // The legacy resolution API will use the aspect ratio of the target size to
- // be the fallback target aspect ratio value when the use case has no target
- // aspect ratio setting.
- val targetSize = getTargetSize(imageOutputConfig)
- if (targetSize != null) {
- outputRatio = getAspectRatioGroupKeyOfTargetSize(
- targetSize,
- resolutionCandidateList
- )
- }
- }
- return outputRatio
- }
-
- /**
- * Removes unnecessary sizes by target size.
- *
- *
- * If the target resolution is set, a size that is equal to or closest to the target
- * resolution will be selected. If the list includes more than one size equal to or larger
- * than the target resolution, only one closest size needs to be kept. The other larger sizes
- * can be removed so that they won't be selected to use.
- *
- * @param supportedSizesList The list should have been sorted in descending order.
- * @param targetSize The target size used to remove unnecessary sizes.
- */
- private fun removeSupportedSizesByTargetSize(
- supportedSizesList: MutableList<Size>?,
- targetSize: Size
- ) {
- if (supportedSizesList == null || supportedSizesList.isEmpty()) {
- return
- }
- var indexBigEnough = -1
- val removeSizes: MutableList<Size> = ArrayList()
-
- // Get the index of the item that is equal to or closest to the target size.
- for (i in supportedSizesList.indices) {
- val outputSize = supportedSizesList[i]
- if (outputSize.width >= targetSize.width && outputSize.height >= targetSize.height) {
- // New big enough item closer to the target size is found. Adding the previous
- // one into the sizes list that will be removed.
- if (indexBigEnough >= 0) {
- removeSizes.add(supportedSizesList[indexBigEnough])
- }
- indexBigEnough = i
- } else {
- break
- }
- }
- // Remove the unnecessary items that are larger than the item closest to the target size.
- supportedSizesList.removeAll(removeSizes)
- }
-
- /**
- * Groups sizes together according to their aspect ratios.
- */
- private fun groupSizesByAspectRatio(sizes: List<Size>): Map<Rational, MutableList<Size>> {
- val aspectRatioSizeListMap: MutableMap<Rational, MutableList<Size>> = mutableMapOf()
-
- val aspectRatioKeys = getResolutionListGroupingAspectRatioKeys(sizes)
-
- aspectRatioKeys.forEach {
- aspectRatioSizeListMap[it] = mutableListOf()
- }
-
- sizes.forEach { size ->
- aspectRatioSizeListMap.keys.forEach { aspectRatio ->
- // Put the size into all groups that is matched in mod16 condition since a size
- // may match multiple aspect ratio in mod16 algorithm.
- if (hasMatchingAspectRatio(size, aspectRatio)) {
- aspectRatioSizeListMap[aspectRatio]?.add(size)
- }
- }
- }
- return aspectRatioSizeListMap
- }
-
- /**
- * Obtains the supported sizes for a given user case.
- */
- internal fun getSupportedOutputSizes(config: UseCaseConfig<*>): List<Size> {
- val imageFormat = config.inputFormat
- val imageOutputConfig = config as ImageOutputConfig
- val customOrderedResolutions = imageOutputConfig.getCustomOrderedResolutions(null)
- if (customOrderedResolutions != null) {
- return customOrderedResolutions
- }
- var outputSizes: Array<Size>? =
- getCustomizedSupportSizesFromConfig(imageFormat, imageOutputConfig)
- if (outputSizes == null) {
- outputSizes = getAllOutputSizesByFormat(imageFormat)
- }
- val outputSizeCandidates: MutableList<Size> = ArrayList()
- var maxSize = imageOutputConfig.getMaxResolution(null)
- val maxOutputSizeByFormat: Size = getMaxOutputSizeByFormat(imageFormat)
-
- // Set maxSize as the max resolution setting or the max supported output size for the
- // image format, whichever is smaller.
- if (maxSize == null ||
- SizeUtil.getArea(maxOutputSizeByFormat) < SizeUtil.getArea(maxSize)
- ) {
- maxSize = maxOutputSizeByFormat
- }
-
- // Sort the output sizes. The Comparator result must be reversed to have a descending order
- // result.
- Arrays.sort(outputSizes, CompareSizesByArea(true))
- var targetSize: Size? = getTargetSize(imageOutputConfig)
- var minSize = RESOLUTION_VGA
- val defaultSizeArea = SizeUtil.getArea(RESOLUTION_VGA)
- val maxSizeArea = SizeUtil.getArea(maxSize)
- // When maxSize is smaller than 640x480, set minSize as 0x0. It means the min size bound
- // will be ignored. Otherwise, set the minimal size according to min(DEFAULT_SIZE,
- // TARGET_RESOLUTION).
- if (maxSizeArea < defaultSizeArea) {
- minSize = SizeUtil.RESOLUTION_ZERO
- } else if (targetSize != null && SizeUtil.getArea(targetSize) < defaultSizeArea) {
- minSize = targetSize
- }
-
- // Filter out the ones that exceed the maximum size and the minimum size. The output
- // sizes candidates list won't have duplicated items.
- for (outputSize: Size in outputSizes) {
- if (SizeUtil.getArea(outputSize) <= SizeUtil.getArea(maxSize) &&
- SizeUtil.getArea(outputSize) >= SizeUtil.getArea(minSize!!) &&
- !outputSizeCandidates.contains(outputSize)
- ) {
- outputSizeCandidates.add(outputSize)
- }
- }
- if (outputSizeCandidates.isEmpty()) {
- throw java.lang.IllegalArgumentException(
- "Can not get supported output size under supported maximum for the format: " +
- imageFormat
- )
- }
-
- val aspectRatio: Rational? = getTargetAspectRatio(imageOutputConfig, outputSizeCandidates)
-
- // Check the default resolution if the target resolution is not set
- targetSize = targetSize ?: imageOutputConfig.getDefaultResolution(null)
- var supportedResolutions: MutableList<Size> = ArrayList()
- var aspectRatioSizeListMap: Map<Rational, MutableList<Size>>
- if (aspectRatio == null) {
- // If no target aspect ratio is set, all sizes can be added to the result list
- // directly. No need to sort again since the source list has been sorted previously.
- supportedResolutions.addAll(outputSizeCandidates)
-
- // If the target resolution is set, use it to remove unnecessary larger sizes.
- targetSize?.let { removeSupportedSizesByTargetSize(supportedResolutions, it) }
- } else {
- // Rearrange the supported size to put the ones with the same aspect ratio in the front
- // of the list and put others in the end from large to small. Some low end devices may
- // not able to get an supported resolution that match the preferred aspect ratio.
-
- // Group output sizes by aspect ratio.
- aspectRatioSizeListMap = groupSizesByAspectRatio(outputSizeCandidates)
-
- // If the target resolution is set, use it to remove unnecessary larger sizes.
- if (targetSize != null) {
- // Remove unnecessary larger sizes from each aspect ratio size list
- for (key: Rational? in aspectRatioSizeListMap.keys) {
- removeSupportedSizesByTargetSize(aspectRatioSizeListMap[key], targetSize)
- }
- }
-
- // Sort the aspect ratio key set by the target aspect ratio.
- val aspectRatios: List<Rational?> = ArrayList(aspectRatioSizeListMap.keys)
- val fullFovRatio = if (activeArraySize != null) {
- Rational(activeArraySize.width(), activeArraySize.height())
- } else {
- null
- }
- Collections.sort(
- aspectRatios,
- CompareAspectRatiosByMappingAreaInFullFovAspectRatioSpace(
- aspectRatio,
- fullFovRatio
- )
- )
-
- // Put available sizes into final result list by aspect ratio distance to target ratio.
- for (rational: Rational? in aspectRatios) {
- for (size: Size in aspectRatioSizeListMap[rational]!!) {
- // A size may exist in multiple groups in mod16 condition. Keep only one in
- // the final list.
- if (!supportedResolutions.contains(size)) {
- supportedResolutions.add(size)
- }
- }
- }
- }
-
- // TODO(b/245619094): Use ExtraCroppingQuirk to insert selected resolutions
-
- return supportedResolutions
- }
-
- /**
* Given all supported output sizes, lists out all possible size arrangements.
*/
private fun getAllPossibleSizeArrangements(
diff --git a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/adapter/SupportedSurfaceCombinationTest.kt b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/adapter/SupportedSurfaceCombinationTest.kt
index 11ce161..2f82f0a 100644
--- a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/adapter/SupportedSurfaceCombinationTest.kt
+++ b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/adapter/SupportedSurfaceCombinationTest.kt
@@ -28,10 +28,8 @@
import android.media.CamcorderProfile.QUALITY_720P
import android.media.MediaRecorder
import android.os.Build
-import android.util.Pair
-import android.util.Rational
+import android.util.Range
import android.util.Size
-import android.view.Surface
import android.view.WindowManager
import androidx.camera.camera2.pipe.CameraId
import androidx.camera.camera2.pipe.integration.CameraPipeConfig
@@ -45,58 +43,41 @@
import androidx.camera.camera2.pipe.testing.FakeCameraBackend
import androidx.camera.camera2.pipe.testing.FakeCameraDevices
import androidx.camera.camera2.pipe.testing.FakeCameraMetadata
-import androidx.camera.core.AspectRatio
import androidx.camera.core.CameraSelector
import androidx.camera.core.CameraSelector.LensFacing
import androidx.camera.core.CameraX
import androidx.camera.core.CameraXConfig
-import androidx.camera.core.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.concurrent.CameraCoordinator
+import androidx.camera.core.impl.AttachedSurfaceInfo
import androidx.camera.core.impl.CameraThreadConfig
import androidx.camera.core.impl.EncoderProfilesProxy
import androidx.camera.core.impl.EncoderProfilesProxy.VideoProfileProxy
-import androidx.camera.core.impl.MutableStateObservable
-import androidx.camera.core.impl.Observable
-import androidx.camera.core.impl.StreamSpec
+import androidx.camera.core.impl.ImageFormatConstants
import androidx.camera.core.impl.SurfaceCombination
import androidx.camera.core.impl.SurfaceConfig
import androidx.camera.core.impl.UseCaseConfig
import androidx.camera.core.impl.UseCaseConfigFactory
-import androidx.camera.core.impl.utils.CompareSizesByArea
-import androidx.camera.core.impl.utils.executor.CameraXExecutors
import androidx.camera.core.internal.utils.SizeUtil
import androidx.camera.core.internal.utils.SizeUtil.RESOLUTION_1440P
+import androidx.camera.core.internal.utils.SizeUtil.RESOLUTION_VGA
import androidx.camera.testing.CameraUtil
import androidx.camera.testing.CameraXUtil
-import androidx.camera.testing.Configs
import androidx.camera.testing.EncoderProfilesUtil
-import androidx.camera.testing.SurfaceTextureProvider
import androidx.camera.testing.fakes.FakeCamera
import androidx.camera.testing.fakes.FakeCameraCoordinator
import androidx.camera.testing.fakes.FakeCameraFactory
import androidx.camera.testing.fakes.FakeCameraInfoInternal
import androidx.camera.testing.fakes.FakeEncoderProfilesProvider
import androidx.camera.testing.fakes.FakeUseCaseConfig
-import androidx.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 androidx.test.platform.app.InstrumentationRegistry
import androidx.testutils.assertThrows
import com.google.common.truth.Truth.assertThat
-import java.util.Arrays
import java.util.concurrent.ExecutionException
import java.util.concurrent.TimeUnit
import org.junit.After
+import org.junit.Assert
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -116,24 +97,15 @@
@DoNotInstrument
@Config(minSdk = Build.VERSION_CODES.LOLLIPOP)
class SupportedSurfaceCombinationTest {
- private val sensorOrientation0 = 0
private val sensorOrientation90 = 90
- private val aspectRatio43 = Rational(4, 3)
- private val aspectRatio169 = Rational(16, 9)
private val landscapePixelArraySize = Size(4032, 3024)
- private val portraitPixelArraySize = Size(3024, 4032)
private val displaySize = Size(720, 1280)
private val vgaSize = Size(640, 480)
- private val vgaSizeStreamSpec = StreamSpec.builder(vgaSize).build()
private val previewSize = Size(1280, 720)
- private val previewSizeStreamSpec = StreamSpec.builder(previewSize).build()
private val recordSize = Size(3840, 2160)
- private val recordSizeStreamSpec = StreamSpec.builder(recordSize).build()
private val maximumSize = Size(4032, 3024)
- private val maximumSizeStreamSpec = StreamSpec.builder(maximumSize).build()
private val legacyVideoMaximumVideoSize = Size(1920, 1080)
private val mod16Size = Size(960, 544)
- private val mod16SizeStreamSpec = StreamSpec.builder(mod16Size).build()
private val profileUhd = EncoderProfilesUtil.createFakeEncoderProfilesProxy(
recordSize.width, recordSize.height
)
@@ -157,9 +129,6 @@
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.
)
private val context = InstrumentationRegistry.getInstrumentation().context
private var cameraFactory: FakeCameraFactory? = null
@@ -194,6 +163,12 @@
CameraXUtil.shutdown()[10000, TimeUnit.MILLISECONDS]
}
+ // //////////////////////////////////////////////////////////////////////////////////////////
+ //
+ // Surface combination support tests for guaranteed configurations
+ //
+ // //////////////////////////////////////////////////////////////////////////////////////////
+
@Test
fun checkLegacySurfaceCombinationSupportedInLegacyDevice() {
setupCamera(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY)
@@ -519,614 +494,11 @@
assertThat(isSupported).isTrue()
}
- @Test
- fun checkTargetAspectRatio() {
- setupCamera(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY)
- val supportedSurfaceCombination = SupportedSurfaceCombination(
- context, fakeCameraMetadata,
- mockEncoderProfilesAdapter
- )
- val fakeUseCase = FakeUseCaseConfig.Builder()
- .setTargetAspectRatio(AspectRatio.RATIO_16_9)
- .build()
- val useCases: MutableList<UseCase> = ArrayList()
- useCases.add(fakeUseCase)
- val useCaseToConfigMap = Configs.useCaseConfigMapWithDefaultSettingsFromUseCaseList(
- cameraFactory!!.getCamera(fakeCameraMetadata.camera.value).cameraInfoInternal,
- useCases,
- useCaseConfigFactory
- )
- val suggestedStreamSpecMap = supportedSurfaceCombination.getSuggestedStreamSpecifications(
- false,
- emptyList(),
- ArrayList(useCaseToConfigMap.values)
- )
- val selectedStreamSpec = suggestedStreamSpecMap[useCaseToConfigMap[fakeUseCase]]!!
- val resultAspectRatio = Rational(
- selectedStreamSpec.resolution.width,
- selectedStreamSpec.resolution.height
- )
- assertThat(resultAspectRatio).isEqualTo(aspectRatio169)
- }
-
- @Test
- fun checkResolutionForMixedUseCase_AfterBindToLifecycle() {
- setupCamera(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY)
-
- // The test case make sure the selected result is expected after the regular flow.
- val targetAspectRatio = aspectRatio169
- val preview = Preview.Builder()
- .setTargetAspectRatio(AspectRatio.RATIO_16_9)
- .build()
- preview.setSurfaceProvider(
- CameraXExecutors.directExecutor(),
- SurfaceTextureProvider.createSurfaceTextureProvider(mock())
- )
- val imageCapture = ImageCapture.Builder()
- .setTargetAspectRatio(AspectRatio.RATIO_16_9)
- .build()
- val imageAnalysis = ImageAnalysis.Builder()
- .setTargetAspectRatio(AspectRatio.RATIO_16_9)
- .build()
- val cameraUseCaseAdapter = CameraUtil
- .createCameraUseCaseAdapter(
- context,
- cameraCoordinator,
- CameraSelector.DEFAULT_BACK_CAMERA
- )
- cameraUseCaseAdapter.addUseCases(listOf(preview, imageCapture, imageAnalysis))
- val previewResolution = preview.attachedSurfaceResolution!!
- val previewRatio = Rational(
- previewResolution.width,
- previewResolution.height
- )
- val imageCaptureResolution = preview.attachedSurfaceResolution
- val imageCaptureRatio = Rational(
- imageCaptureResolution!!.width,
- imageCaptureResolution.height
- )
- val imageAnalysisResolution = preview.attachedSurfaceResolution
- val imageAnalysisRatio = Rational(
- imageAnalysisResolution!!.width,
- imageAnalysisResolution.height
- )
-
- // Checks no correction is needed.
- assertThat(previewRatio).isEqualTo(targetAspectRatio)
- assertThat(imageCaptureRatio).isEqualTo(targetAspectRatio)
- assertThat(imageAnalysisRatio).isEqualTo(targetAspectRatio)
- }
-
- @Test
- fun checkDefaultAspectRatioAndResolutionForMixedUseCase() {
- setupCamera(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED)
- val supportedSurfaceCombination = SupportedSurfaceCombination(
- context, fakeCameraMetadata,
- mockEncoderProfilesAdapter
- )
- val preview = Preview.Builder().build()
- preview.setSurfaceProvider(
- CameraXExecutors.directExecutor(),
- SurfaceTextureProvider.createSurfaceTextureProvider(mock())
- )
- val imageCapture = ImageCapture.Builder().build()
- val imageAnalysis = ImageAnalysis.Builder().build()
-
- // 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 cameraUseCaseAdapter = CameraUtil.createCameraUseCaseAdapter(
- context,
- cameraCoordinator,
- CameraSelector.DEFAULT_BACK_CAMERA
- )
- cameraUseCaseAdapter.addUseCases(
- listOf(
- preview,
- imageCapture, imageAnalysis
- )
- )
- val useCases: MutableList<UseCase> = ArrayList()
- useCases.add(preview)
- useCases.add(imageCapture)
- useCases.add(imageAnalysis)
- val useCaseToConfigMap = Configs.useCaseConfigMapWithDefaultSettingsFromUseCaseList(
- cameraFactory!!.getCamera(fakeCameraMetadata.camera.value).cameraInfoInternal,
- useCases,
- useCaseConfigFactory
- )
- val suggestedStreamSpecMap = supportedSurfaceCombination.getSuggestedStreamSpecifications(
- false,
- emptyList(),
- ArrayList(useCaseToConfigMap.values)
- )
- val previewSize = suggestedStreamSpecMap[useCaseToConfigMap[preview]]!!.resolution
- val imageCaptureSize = suggestedStreamSpecMap[useCaseToConfigMap[imageCapture]]!!.resolution
- val imageAnalysisSize =
- suggestedStreamSpecMap[useCaseToConfigMap[imageAnalysis]]!!.resolution
-
- 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(aspectRatio43)
- assertThat(imageCaptureAspectRatio).isEqualTo(aspectRatio43)
- assertThat(imageAnalysisAspectRatio).isEqualTo(aspectRatio43)
-
- // Checks the default resolution.
- assertThat(imageAnalysisSize).isEqualTo(vgaSize)
- }
-
- @Test
- fun checkSmallSizesAreFilteredOutByDefaultSize480p() {
- setupCamera(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED)
- val supportedSurfaceCombination = SupportedSurfaceCombination(
- context, fakeCameraMetadata,
- mockEncoderProfilesAdapter
- )
-
- /* 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 = Preview.Builder()
- .setTargetResolution(Size(displayHeight, displayWidth))
- .build()
- val useCases: MutableList<UseCase> = ArrayList()
- useCases.add(preview)
- val useCaseToConfigMap = Configs.useCaseConfigMapWithDefaultSettingsFromUseCaseList(
- cameraFactory!!.getCamera(fakeCameraMetadata.camera.value).cameraInfoInternal,
- useCases,
- useCaseConfigFactory
- )
- val suggestedStreamSpecMap = supportedSurfaceCombination.getSuggestedStreamSpecifications(
- false,
- emptyList(),
- ArrayList(useCaseToConfigMap.values)
- )
-
- // Checks the preconditions.
- val preconditionSize = Size(256, 144)
- val targetRatio = Rational(displayHeight, displayWidth)
- val sizeList = ArrayList(listOf(*supportedSizes))
- assertThat(sizeList).contains(preconditionSize)
- for (s in supportedSizes) {
- val supportedRational = Rational(s.width, s.height)
- assertThat(supportedRational).isNotEqualTo(targetRatio)
- }
-
- // Checks the mechanism has filtered out the sizes which are smaller than default size
- // 480p.
- val previewSize = suggestedStreamSpecMap[useCaseToConfigMap[preview]]
- assertThat(previewSize).isNotEqualTo(preconditionSize)
- }
-
- @Test
- fun checkAspectRatioMatchedSizeCanBeSelected() {
- setupCamera(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED)
- val supportedSurfaceCombination = SupportedSurfaceCombination(
- context, fakeCameraMetadata,
- mockEncoderProfilesAdapter
- )
-
- // 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.
- for (targetResolution in supportedSizes) {
- val imageCapture = ImageCapture.Builder().setTargetResolution(
- targetResolution
- ).setTargetRotation(Surface.ROTATION_90).build()
- val suggestedStreamSpecMap =
- supportedSurfaceCombination.getSuggestedStreamSpecifications(
- false,
- emptyList(),
- listOf(imageCapture.currentConfig)
- )
- assertThat(targetResolution).isEqualTo(
- suggestedStreamSpecMap[imageCapture.currentConfig]?.resolution
- )
- }
- }
-
- @Test
- fun checkCorrectAspectRatioNotMatchedSizeCanBeSelected() {
- setupCamera(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED)
- val supportedSurfaceCombination = SupportedSurfaceCombination(
- context, fakeCameraMetadata,
- mockEncoderProfilesAdapter
- )
-
- // 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 targetResolution = Size(1280, 640)
- val imageCapture = ImageCapture.Builder().setTargetResolution(
- targetResolution
- ).setTargetRotation(Surface.ROTATION_90).build()
- val suggestedStreamSpecMap = supportedSurfaceCombination.getSuggestedStreamSpecifications(
- false,
- emptyList(),
- listOf(imageCapture.currentConfig)
- )
- assertThat(Size(1280, 720)).isEqualTo(
- suggestedStreamSpecMap[imageCapture.currentConfig]?.resolution
- )
- }
-
- @Test
- fun suggestedStreamSpecsForMixedUseCaseNotSupportedInLegacyDevice() {
- setupCamera(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY)
- val supportedSurfaceCombination = SupportedSurfaceCombination(
- context, fakeCameraMetadata,
- mockEncoderProfilesAdapter
- )
- val imageCapture = ImageCapture.Builder()
- .setTargetAspectRatio(AspectRatio.RATIO_16_9)
- .build()
- val videoCapture = createVideoCapture()
- val preview = Preview.Builder()
- .setTargetAspectRatio(AspectRatio.RATIO_16_9)
- .build()
- val useCases: MutableList<UseCase> = ArrayList()
- useCases.add(imageCapture)
- useCases.add(videoCapture)
- useCases.add(preview)
- val useCaseToConfigMap = Configs.useCaseConfigMapWithDefaultSettingsFromUseCaseList(
- cameraFactory!!.getCamera(fakeCameraMetadata.camera.value).cameraInfoInternal,
- useCases,
- useCaseConfigFactory
- )
- assertThrows(IllegalArgumentException::class.java) {
- supportedSurfaceCombination.getSuggestedStreamSpecifications(
- false,
- emptyList(),
- ArrayList(useCaseToConfigMap.values)
- )
- }
- }
-
- @Test
- fun suggestedStreamSpecsForCustomizeResolutionsNotSupportedInLegacyDevice() {
- setupCamera(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY)
- val supportedSurfaceCombination = SupportedSurfaceCombination(
- context, fakeCameraMetadata,
- mockEncoderProfilesAdapter
- )
-
- // Legacy camera only support (PRIV, PREVIEW) + (PRIV, PREVIEW)
- val quality = Quality.UHD
- val previewResolutionsPairs = listOf(
- Pair.create(ImageFormat.PRIVATE, arrayOf(previewSize))
- )
- val videoCapture = createVideoCapture(quality)
- val preview = Preview.Builder()
- .setSupportedResolutions(previewResolutionsPairs)
- .build()
- val useCases: MutableList<UseCase> = ArrayList()
- useCases.add(videoCapture)
- useCases.add(preview)
- val useCaseToConfigMap = Configs.useCaseConfigMapWithDefaultSettingsFromUseCaseList(
- cameraFactory!!.getCamera(fakeCameraMetadata.camera.value).cameraInfoInternal,
- useCases,
- useCaseConfigFactory
- )
- assertThrows(IllegalArgumentException::class.java) {
- supportedSurfaceCombination.getSuggestedStreamSpecifications(
- false,
- emptyList(),
- ArrayList(useCaseToConfigMap.values)
- )
- }
- }
-
- // (PRIV, PREVIEW) + (PRIV, RECORD) + (JPEG, RECORD)
- @Test
- fun suggestedStreamSpecsForMixedUseCaseInLimitedDevice() {
- setupCamera(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED)
- val supportedSurfaceCombination = SupportedSurfaceCombination(
- context, fakeCameraMetadata,
- mockEncoderProfilesAdapter
- )
- val imageCapture = ImageCapture.Builder()
- .setTargetAspectRatio(AspectRatio.RATIO_16_9)
- .build()
- val videoCapture = createVideoCapture(Quality.HIGHEST)
- val preview = Preview.Builder()
- .setTargetAspectRatio(AspectRatio.RATIO_16_9)
- .build()
- val useCases: MutableList<UseCase> = ArrayList()
- useCases.add(imageCapture)
- useCases.add(videoCapture)
- useCases.add(preview)
- val useCaseToConfigMap = Configs.useCaseConfigMapWithDefaultSettingsFromUseCaseList(
- cameraFactory!!.getCamera(fakeCameraMetadata.camera.value).cameraInfoInternal,
- useCases,
- useCaseConfigFactory
- )
- val suggestedStreamSpecMap: Map<UseCaseConfig<*>, StreamSpec> =
- supportedSurfaceCombination.getSuggestedStreamSpecifications(
- false,
- emptyList(),
- ArrayList(useCaseToConfigMap.values)
- )
-
- // (PRIV, PREVIEW) + (PRIV, RECORD) + (JPEG, RECORD)
- assertThat(suggestedStreamSpecMap).containsEntry(
- useCaseToConfigMap[imageCapture],
- recordSizeStreamSpec
- )
- assertThat(suggestedStreamSpecMap).containsEntry(
- useCaseToConfigMap[videoCapture],
- recordSizeStreamSpec
- )
- assertThat(suggestedStreamSpecMap).containsEntry(
- useCaseToConfigMap[preview],
- previewSizeStreamSpec
- )
- }
-
- @Test
- fun suggestedStreamSpecsInFullDevice_videoHasHigherPriorityThanImage() {
- setupCamera(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL)
- val supportedSurfaceCombination = SupportedSurfaceCombination(
- context, fakeCameraMetadata,
- mockEncoderProfilesAdapter
- )
- val imageCapture = ImageCapture.Builder()
- .setTargetAspectRatio(AspectRatio.RATIO_16_9)
- .build()
- val videoCapture = createVideoCapture(
- QualitySelector.from(
- Quality.UHD,
- FallbackStrategy.lowerQualityOrHigherThan(Quality.UHD)
- )
- )
- val preview = Preview.Builder()
- .setTargetAspectRatio(AspectRatio.RATIO_16_9)
- .build()
- val useCases: MutableList<UseCase> = ArrayList()
- useCases.add(imageCapture)
- useCases.add(videoCapture)
- useCases.add(preview)
- val useCaseToConfigMap = Configs.useCaseConfigMapWithDefaultSettingsFromUseCaseList(
- cameraFactory!!.getCamera(fakeCameraMetadata.camera.value).cameraInfoInternal,
- useCases,
- useCaseConfigFactory
- )
- val suggestedStreamSpecMap: Map<UseCaseConfig<*>, StreamSpec> =
- supportedSurfaceCombination.getSuggestedStreamSpecifications(
- false,
- emptyList(),
- ArrayList(useCaseToConfigMap.values)
- )
-
- // 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(suggestedStreamSpecMap).containsEntry(
- useCaseToConfigMap[imageCapture],
- recordSizeStreamSpec
- )
- assertThat(suggestedStreamSpecMap).containsEntry(
- useCaseToConfigMap[videoCapture],
- recordSizeStreamSpec
- )
- assertThat(suggestedStreamSpecMap).containsEntry(
- useCaseToConfigMap[preview],
- previewSizeStreamSpec
- )
- }
-
- @Test
- fun suggestedResInFullDevice_videoRecordSizeLowPriority_imageCanGetMaxSize() {
- setupCamera(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL)
- val supportedSurfaceCombination = SupportedSurfaceCombination(
- context, fakeCameraMetadata,
- mockEncoderProfilesAdapter
- )
- val imageCapture = ImageCapture.Builder()
- .setTargetAspectRatio(AspectRatio.RATIO_4_3) // mMaximumSize(4032x3024) is 4:3
- .build()
- val videoCapture = createVideoCapture(
- QualitySelector.fromOrderedList(
- listOf(Quality.HD, Quality.FHD, Quality.UHD)
- )
- )
- val preview = Preview.Builder()
- .setTargetAspectRatio(AspectRatio.RATIO_16_9)
- .build()
- val useCases: MutableList<UseCase> = ArrayList()
- useCases.add(imageCapture)
- useCases.add(videoCapture)
- useCases.add(preview)
- val useCaseToConfigMap = Configs.useCaseConfigMapWithDefaultSettingsFromUseCaseList(
- cameraFactory!!.getCamera(fakeCameraMetadata.camera.value).cameraInfoInternal,
- useCases,
- useCaseConfigFactory
- )
- val suggestedStreamSpecMap: Map<UseCaseConfig<*>, StreamSpec> =
- supportedSurfaceCombination.getSuggestedStreamSpecifications(
- false,
- emptyList(),
- ArrayList(useCaseToConfigMap.values)
- )
-
- // 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(suggestedStreamSpecMap).containsEntry(
- useCaseToConfigMap[imageCapture],
- maximumSizeStreamSpec
- )
- assertThat(suggestedStreamSpecMap).containsEntry(
- useCaseToConfigMap[videoCapture],
- previewSizeStreamSpec
- ) // Quality.HD
- assertThat(suggestedStreamSpecMap).containsEntry(
- useCaseToConfigMap[preview],
- previewSizeStreamSpec
- )
- }
-
- @Test
- fun suggestedStreamSpecsWithSameSupportedListForDifferentUseCases() {
- setupCamera(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL)
- val supportedSurfaceCombination = SupportedSurfaceCombination(
- context, fakeCameraMetadata,
- mockEncoderProfilesAdapter
- )
-
- /* 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 = ImageCapture.Builder()
- .setTargetResolution(displaySize)
- .build()
- val preview = Preview.Builder()
- .setTargetResolution(displaySize)
- .build()
- val imageAnalysis = ImageAnalysis.Builder()
- .setTargetResolution(displaySize)
- .build()
- val useCases: MutableList<UseCase> = ArrayList()
- useCases.add(imageCapture)
- useCases.add(preview)
- useCases.add(imageAnalysis)
- val useCaseToConfigMap = Configs.useCaseConfigMapWithDefaultSettingsFromUseCaseList(
- cameraFactory!!.getCamera(fakeCameraMetadata.camera.value).cameraInfoInternal,
- useCases,
- useCaseConfigFactory
- )
- val suggestedStreamSpecMap: Map<UseCaseConfig<*>, StreamSpec> =
- supportedSurfaceCombination.getSuggestedStreamSpecifications(
- false,
- emptyList(),
- ArrayList(useCaseToConfigMap.values)
- )
- assertThat(suggestedStreamSpecMap).containsEntry(
- useCaseToConfigMap[imageCapture],
- previewSizeStreamSpec
- )
- assertThat(suggestedStreamSpecMap).containsEntry(
- useCaseToConfigMap[preview],
- previewSizeStreamSpec
- )
- assertThat(suggestedStreamSpecMap).containsEntry(
- useCaseToConfigMap[imageAnalysis],
- previewSizeStreamSpec
- )
- }
-
- @Test
- fun throwsWhenSetBothTargetResolutionAndAspectRatioForDifferentUseCases() {
- setupCamera(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY)
- var previewExceptionHappened = false
- val previewBuilder = Preview.Builder()
- .setTargetResolution(displaySize)
- .setTargetAspectRatio(AspectRatio.RATIO_16_9)
- try {
- previewBuilder.build()
- } catch (e: IllegalArgumentException) {
- previewExceptionHappened = true
- }
- assertThat(previewExceptionHappened).isTrue()
- var imageCaptureExceptionHappened = false
- val imageCaptureConfigBuilder = ImageCapture.Builder()
- .setTargetResolution(displaySize)
- .setTargetAspectRatio(AspectRatio.RATIO_16_9)
- try {
- imageCaptureConfigBuilder.build()
- } catch (e: IllegalArgumentException) {
- imageCaptureExceptionHappened = true
- }
- assertThat(imageCaptureExceptionHappened).isTrue()
- var imageAnalysisExceptionHappened = false
- val imageAnalysisConfigBuilder = ImageAnalysis.Builder()
- .setTargetResolution(displaySize)
- .setTargetAspectRatio(AspectRatio.RATIO_16_9)
- try {
- imageAnalysisConfigBuilder.build()
- } catch (e: IllegalArgumentException) {
- imageAnalysisExceptionHappened = true
- }
- assertThat(imageAnalysisExceptionHappened).isTrue()
- }
-
- @Test
- fun suggestedStreamSpecsForCustomizedSupportedResolutions() {
-
- // Checks all suggested stream specs will have their resolutions become 640x480.
- setupCamera(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED)
- val supportedSurfaceCombination = SupportedSurfaceCombination(
- context, fakeCameraMetadata,
- mockEncoderProfilesAdapter
- )
- val formatResolutionsPairList: MutableList<Pair<Int, Array<Size>>> = ArrayList()
- formatResolutionsPairList.add(Pair.create(ImageFormat.JPEG, arrayOf(vgaSize)))
- formatResolutionsPairList.add(
- Pair.create(ImageFormat.YUV_420_888, arrayOf(vgaSize))
- )
- formatResolutionsPairList.add(Pair.create(ImageFormat.PRIVATE, arrayOf(vgaSize)))
-
- // Sets use cases customized supported resolutions to 640x480 only.
- val imageCapture = ImageCapture.Builder()
- .setSupportedResolutions(formatResolutionsPairList)
- .build()
- val videoCapture = createVideoCapture(Quality.SD)
- val preview = Preview.Builder()
- .setSupportedResolutions(formatResolutionsPairList)
- .build()
- val useCases: MutableList<UseCase> = ArrayList()
- useCases.add(imageCapture)
- useCases.add(videoCapture)
- useCases.add(preview)
- val useCaseToConfigMap = Configs.useCaseConfigMapWithDefaultSettingsFromUseCaseList(
- cameraFactory!!.getCamera(fakeCameraMetadata.camera.value).cameraInfoInternal,
- useCases,
- useCaseConfigFactory
- )
- val suggestedStreamSpecMap: Map<UseCaseConfig<*>, StreamSpec> =
- supportedSurfaceCombination.getSuggestedStreamSpecifications(
- false,
- emptyList(),
- ArrayList(useCaseToConfigMap.values)
- )
-
- // Checks all suggested stream specs will have their resolutions become 640x480.
- assertThat(suggestedStreamSpecMap).containsEntry(
- useCaseToConfigMap[imageCapture],
- vgaSizeStreamSpec
- )
- assertThat(suggestedStreamSpecMap).containsEntry(
- useCaseToConfigMap[videoCapture],
- vgaSizeStreamSpec
- )
- assertThat(suggestedStreamSpecMap).containsEntry(
- useCaseToConfigMap[preview],
- vgaSizeStreamSpec
- )
- }
+ // //////////////////////////////////////////////////////////////////////////////////////////
+ //
+ // Surface config transformation tests
+ //
+ // //////////////////////////////////////////////////////////////////////////////////////////
@Test
fun transformSurfaceConfigWithYUVAnalysisSize() {
@@ -1364,6 +736,755 @@
assertThat(surfaceConfig).isEqualTo(expectedSurfaceConfig)
}
+ // //////////////////////////////////////////////////////////////////////////////////////////
+ //
+ // Resolution selection tests for LEGACY-level guaranteed configurations
+ //
+ // //////////////////////////////////////////////////////////////////////////////////////////
+
+ /**
+ * PRIV/MAXIMUM
+ */
+ @Test
+ fun canSelectCorrectSize_singlePrivStream_inLegacyDevice() {
+ val privUseCase = createUseCase(UseCaseConfigFactory.CaptureType.VIDEO_CAPTURE)
+ val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+ put(privUseCase, maximumSize)
+ }
+ getSuggestedSpecsAndVerify(useCaseExpectedResultMap)
+ }
+
+ /**
+ * JPEG/MAXIMUM
+ */
+ @Test
+ fun canSelectCorrectSize_singleJpegStream_inLegacyDevice() {
+ val jpegUseCase = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_CAPTURE)
+ val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+ put(jpegUseCase, maximumSize)
+ }
+ getSuggestedSpecsAndVerify(useCaseExpectedResultMap)
+ }
+
+ /**
+ * YUV/MAXIMUM
+ */
+ @Test
+ fun canSelectCorrectSize_singleYuvStream_inLegacyDevice() {
+ val yuvUseCase = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_ANALYSIS)
+ val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+ put(yuvUseCase, maximumSize)
+ }
+ getSuggestedSpecsAndVerify(useCaseExpectedResultMap)
+ }
+
+ /**
+ * PRIV/PREVIEW + JPEG/MAXIMUM
+ */
+ @Test
+ fun canSelectCorrectSizes_privPlusJpeg_inLegacyDevice() {
+ val privUseCase = createUseCase(UseCaseConfigFactory.CaptureType.PREVIEW)
+ val jpegUseCase = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_CAPTURE)
+ val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+ put(privUseCase, previewSize)
+ put(jpegUseCase, maximumSize)
+ }
+ getSuggestedSpecsAndVerify(useCaseExpectedResultMap)
+ }
+
+ /**
+ * YUV/PREVIEW + JPEG/MAXIMUM
+ */
+ @Test
+ fun canSelectCorrectSizes_yuvPlusJpeg_inLegacyDevice() {
+ val yuvUseCase = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_ANALYSIS) // YUV
+ val jpegUseCase = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_CAPTURE) // JPEG
+ val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+ put(yuvUseCase, previewSize)
+ put(jpegUseCase, maximumSize)
+ }
+ getSuggestedSpecsAndVerify(useCaseExpectedResultMap)
+ }
+
+ /**
+ * PRIV/PREVIEW + PRIV/PREVIEW
+ */
+ @Test
+ fun canSelectCorrectSizes_privPlusPriv_inLegacyDevice() {
+ val privUseCase1 = createUseCase(UseCaseConfigFactory.CaptureType.PREVIEW) // PRIV
+ val privUseCase2 = createUseCase(UseCaseConfigFactory.CaptureType.VIDEO_CAPTURE) // PRIV
+ val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+ put(privUseCase1, previewSize)
+ put(privUseCase2, previewSize)
+ }
+ getSuggestedSpecsAndVerify(useCaseExpectedResultMap)
+ }
+
+ /**
+ * PRIV/PREVIEW + YUV/PREVIEW
+ */
+ @Test
+ fun canSelectCorrectSizes_privPlusYuv_inLegacyDevice() {
+ val privUseCase = createUseCase(UseCaseConfigFactory.CaptureType.PREVIEW) // PRIV
+ val yuvUseCase = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_ANALYSIS) // YUV
+ val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+ put(privUseCase, previewSize)
+ put(yuvUseCase, previewSize)
+ }
+ getSuggestedSpecsAndVerify(useCaseExpectedResultMap)
+ }
+
+ /**
+ * PRIV/PREVIEW + YUV/PREVIEW + JPEG/MAXIMUM
+ */
+ @Test
+ fun canSelectCorrectSizes_privPlusYuvPlusJpeg_inLegacyDevice() {
+ val privUseCase = createUseCase(UseCaseConfigFactory.CaptureType.PREVIEW) // PRIV
+ val yuvUseCase = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_ANALYSIS) // YUV
+ val jpegUseCase = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_CAPTURE) // JPEG
+ val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+ put(privUseCase, previewSize)
+ put(yuvUseCase, previewSize)
+ put(jpegUseCase, maximumSize)
+ }
+ getSuggestedSpecsAndVerify(useCaseExpectedResultMap)
+ }
+
+ /**
+ * Unsupported PRIV + JPEG + PRIV for legacy level devices
+ */
+ @Test
+ fun throwsException_unsupportedConfiguration_inLegacyDevice() {
+ val privUseCase1 = createUseCase(UseCaseConfigFactory.CaptureType.PREVIEW) // PRIV
+ val jpegUseCase = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_CAPTURE) // JPEG
+ val privUseCas2 = createUseCase(UseCaseConfigFactory.CaptureType.VIDEO_CAPTURE) // PRIV
+ val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+ put(privUseCase1, RESOLUTION_VGA)
+ put(jpegUseCase, RESOLUTION_VGA)
+ put(privUseCas2, RESOLUTION_VGA)
+ }
+ Assert.assertThrows(IllegalArgumentException::class.java) {
+ getSuggestedSpecsAndVerify(useCaseExpectedResultMap)
+ }
+ }
+
+ // //////////////////////////////////////////////////////////////////////////////////////////
+ //
+ // Resolution selection tests for LIMITED-level guaranteed configurations
+ //
+ // //////////////////////////////////////////////////////////////////////////////////////////
+
+ /**
+ * PRIV/PREVIEW + PRIV/RECORD
+ */
+ @Test
+ fun canSelectCorrectSizes_privPlusPriv_inLimitedDevice() {
+ val privUseCase1 = createUseCase(UseCaseConfigFactory.CaptureType.VIDEO_CAPTURE) // PRIV
+ val privUseCas2 = createUseCase(UseCaseConfigFactory.CaptureType.PREVIEW) // PRIV
+ val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+ put(privUseCase1, recordSize)
+ put(privUseCas2, previewSize)
+ }
+ getSuggestedSpecsAndVerify(
+ useCaseExpectedResultMap,
+ hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
+ )
+ }
+
+ /**
+ * PRIV/PREVIEW + YUV/RECORD
+ */
+ @Test
+ fun canSelectCorrectSizes_privPlusYuv_inLimitedDevice() {
+ val privUseCase = createUseCase(UseCaseConfigFactory.CaptureType.PREVIEW) // PRIV
+ val yuvUseCase = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_ANALYSIS) // YUV
+ val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+ put(privUseCase, previewSize)
+ put(yuvUseCase, recordSize)
+ }
+ getSuggestedSpecsAndVerify(
+ useCaseExpectedResultMap,
+ hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
+ )
+ }
+
+ /**
+ * YUV/PREVIEW + YUV/RECORD
+ */
+ @Test
+ fun canSelectCorrectSizes_yuvPlusYuv_inLimitedDevice() {
+ val yuvUseCase1 = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_ANALYSIS) // YUV
+ val yuvUseCase2 = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_ANALYSIS) // YUV
+ val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+ put(yuvUseCase1, recordSize)
+ put(yuvUseCase2, previewSize)
+ }
+ getSuggestedSpecsAndVerify(
+ useCaseExpectedResultMap,
+ hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
+ )
+ }
+
+ /**
+ * PRIV/PREVIEW + PRIV/RECORD + JPEG/RECORD
+ */
+ @Test
+ fun canSelectCorrectSizes_privPlusPrivPlusJpeg_inLimitedDevice() {
+ val privUseCase1 = createUseCase(UseCaseConfigFactory.CaptureType.VIDEO_CAPTURE) // PRIV
+ val privUseCase2 = createUseCase(UseCaseConfigFactory.CaptureType.PREVIEW) // PRIV
+ val jpegUseCase = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_CAPTURE) // JPEG
+ val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+ put(privUseCase1, recordSize)
+ put(privUseCase2, previewSize)
+ put(jpegUseCase, recordSize)
+ }
+ getSuggestedSpecsAndVerify(
+ useCaseExpectedResultMap,
+ hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
+ )
+ }
+
+ /**
+ * PRIV/PREVIEW + YUV/RECORD + JPEG/RECORD
+ */
+ @Test
+ fun canSelectCorrectSizes_privPlusYuvPlusJpeg_inLimitedDevice() {
+ val privUseCase = createUseCase(UseCaseConfigFactory.CaptureType.PREVIEW) // PRIV
+ val yuvUseCase = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_ANALYSIS) // YUV
+ val jpegUseCase = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_CAPTURE) // JPEG
+ val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+ put(privUseCase, previewSize)
+ put(yuvUseCase, recordSize)
+ put(jpegUseCase, recordSize)
+ }
+ getSuggestedSpecsAndVerify(
+ useCaseExpectedResultMap,
+ hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
+ )
+ }
+
+ /**
+ * YUV/PREVIEW + YUV/PREVIEW + JPEG/MAXIMUM
+ */
+ @Test
+ fun canSelectCorrectSizes_yuvPlusYuvPlusJpeg_inLimitedDevice() {
+ val yuvUseCase1 = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_ANALYSIS) // YUV
+ val yuvUseCase2 = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_ANALYSIS) // YUV
+ val jpegUseCase = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_CAPTURE) // JPEG
+ val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+ put(yuvUseCase1, previewSize)
+ put(yuvUseCase2, previewSize)
+ put(jpegUseCase, maximumSize)
+ }
+ getSuggestedSpecsAndVerify(
+ useCaseExpectedResultMap,
+ hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
+ )
+ }
+
+ /**
+ * Unsupported YUV + PRIV + YUV for limited level devices
+ */
+ @Test
+ fun throwsException_unsupportedConfiguration_inLimitedDevice() {
+ val yuvUseCase1 = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_ANALYSIS) // YUV
+ val privUseCase = createUseCase(UseCaseConfigFactory.CaptureType.PREVIEW) // PRIV
+ val yuvUseCase2 = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_ANALYSIS) // YUV
+ val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+ put(yuvUseCase1, RESOLUTION_VGA)
+ put(privUseCase, RESOLUTION_VGA)
+ put(yuvUseCase2, RESOLUTION_VGA)
+ }
+ Assert.assertThrows(IllegalArgumentException::class.java) {
+ getSuggestedSpecsAndVerify(
+ useCaseExpectedResultMap,
+ hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
+ )
+ }
+ }
+
+ // //////////////////////////////////////////////////////////////////////////////////////////
+ //
+ // Resolution selection tests for FULL-level guaranteed configurations
+ //
+ // //////////////////////////////////////////////////////////////////////////////////////////
+
+ /**
+ * PRIV/PREVIEW + PRIV/MAXIMUM
+ */
+ @Test
+ fun canSelectCorrectSizes_privPlusPriv_inFullDevice() {
+ val privUseCase1 = createUseCase(UseCaseConfigFactory.CaptureType.VIDEO_CAPTURE) // PRIV
+ val privUseCase2 = createUseCase(UseCaseConfigFactory.CaptureType.PREVIEW) // PRIV
+ val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+ put(privUseCase1, maximumSize)
+ put(privUseCase2, previewSize)
+ }
+ getSuggestedSpecsAndVerify(
+ useCaseExpectedResultMap,
+ hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL
+ )
+ }
+
+ /**
+ * PRIV/PREVIEW + YUV/MAXIMUM
+ */
+ @Test
+ fun canSelectCorrectSizes_privPlusYuv_inFullDevice() {
+ val privUseCase = createUseCase(UseCaseConfigFactory.CaptureType.PREVIEW) // PRIV
+ val yuvUseCase = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_ANALYSIS) // YUV
+ val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+ put(privUseCase, previewSize)
+ put(yuvUseCase, maximumSize)
+ }
+ getSuggestedSpecsAndVerify(
+ useCaseExpectedResultMap,
+ hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL
+ )
+ }
+
+ /**
+ * YUV/PREVIEW + YUV/MAXIMUM
+ */
+ @Test
+ fun canSelectCorrectSizes_yuvPlusYuv_inFullDevice() {
+ val yuvUseCase1 = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_ANALYSIS) // YUV
+ val yuvUseCase2 = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_ANALYSIS) // YUV
+ val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+ put(yuvUseCase1, maximumSize)
+ put(yuvUseCase2, previewSize)
+ }
+ getSuggestedSpecsAndVerify(
+ useCaseExpectedResultMap,
+ hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL
+ )
+ }
+
+ /**
+ * PRIV/PREVIEW + PRIV/PREVIEW + JPEG/MAXIMUM
+ */
+ @Test
+ fun canSelectCorrectSizes_privPlusPrivPlusJpeg_inFullDevice() {
+ val jpegUseCase = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_CAPTURE) // JPEG
+ val privUseCase1 = createUseCase(UseCaseConfigFactory.CaptureType.VIDEO_CAPTURE) // PRIV
+ val privUseCase2 = createUseCase(UseCaseConfigFactory.CaptureType.PREVIEW) // PRIV
+ val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+ put(jpegUseCase, maximumSize)
+ put(privUseCase1, previewSize)
+ put(privUseCase2, previewSize)
+ }
+ getSuggestedSpecsAndVerify(
+ useCaseExpectedResultMap,
+ hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL
+ )
+ }
+
+ /**
+ * YUV/VGA + PRIV/PREVIEW + YUV/MAXIMUM
+ */
+ @Test
+ fun canSelectCorrectSizes_yuvPlusPrivPlusYuv_inFullDevice() {
+ val privUseCase = createUseCase(UseCaseConfigFactory.CaptureType.PREVIEW) // PRIV
+ val yuvUseCase1 = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_ANALYSIS) // YUV
+ val yuvUseCase2 = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_ANALYSIS) // YUV
+ val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+ put(privUseCase, previewSize)
+ put(yuvUseCase1, maximumSize)
+ put(yuvUseCase2, RESOLUTION_VGA)
+ }
+ getSuggestedSpecsAndVerify(
+ useCaseExpectedResultMap,
+ hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL
+ )
+ }
+
+ /**
+ * YUV/VGA + YUV/PREVIEW + YUV/MAXIMUM
+ */
+ @Test
+ fun canSelectCorrectSizes_yuvPlusYuvPlusYuv_inFullDevice() {
+ val yuvUseCase1 = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_ANALYSIS) // YUV
+ val yuvUseCase2 = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_ANALYSIS) // YUV
+ val yuvUseCase3 = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_ANALYSIS) // YUV
+ val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+ put(yuvUseCase1, maximumSize)
+ put(yuvUseCase2, previewSize)
+ put(yuvUseCase3, RESOLUTION_VGA)
+ }
+ getSuggestedSpecsAndVerify(
+ useCaseExpectedResultMap,
+ hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL
+ )
+ }
+
+ /**
+ * Unsupported PRIV + PRIV + YUV + RAW for full level devices
+ */
+ @Test
+ fun throwsException_unsupportedConfiguration_inFullDevice() {
+ val privUseCase1 = createUseCase(UseCaseConfigFactory.CaptureType.VIDEO_CAPTURE) // PRIV
+ val privUseCase2 = createUseCase(UseCaseConfigFactory.CaptureType.PREVIEW) // PRIV
+ val yuvUseCase = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_ANALYSIS) // YUV
+ val rawUseCase = createRawUseCase() // RAW
+ val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+ put(privUseCase1, RESOLUTION_VGA)
+ put(privUseCase2, RESOLUTION_VGA)
+ put(yuvUseCase, RESOLUTION_VGA)
+ put(rawUseCase, RESOLUTION_VGA)
+ }
+ assertThrows(IllegalArgumentException::class.java) {
+ getSuggestedSpecsAndVerify(
+ useCaseExpectedResultMap,
+ hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
+ )
+ }
+ }
+
+ // //////////////////////////////////////////////////////////////////////////////////////////
+ //
+ // Resolution selection tests for Level-3 guaranteed configurations
+ //
+ // //////////////////////////////////////////////////////////////////////////////////////////
+
+ /**
+ * PRIV/PREVIEW + PRIV/VGA + YUV/MAXIMUM + RAW/MAXIMUM
+ */
+ @Test
+ fun canSelectCorrectSizes_privPlusPrivPlusYuvPlusRaw_inLevel3Device() {
+ val privUseCase1 = createUseCase(UseCaseConfigFactory.CaptureType.VIDEO_CAPTURE) // PRIV
+ val privUseCase2 = createUseCase(UseCaseConfigFactory.CaptureType.PREVIEW) // PRIV
+ val yuvUseCase = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_ANALYSIS) // YUV
+ val rawUseCase = createRawUseCase() // RAW
+ val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+ put(privUseCase1, previewSize)
+ put(privUseCase2, RESOLUTION_VGA)
+ put(yuvUseCase, maximumSize)
+ put(rawUseCase, maximumSize)
+ }
+ getSuggestedSpecsAndVerify(
+ useCaseExpectedResultMap,
+ hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_3
+ )
+ }
+
+ /**
+ * PRIV/PREVIEW + PRIV/VGA + JPEG/MAXIMUM + RAW/MAXIMUM
+ */
+ @Test
+ fun canSelectCorrectSizes_privPlusPrivPlusJpegPlusRaw_inLevel3Device() {
+ val privUseCase1 = createUseCase(UseCaseConfigFactory.CaptureType.VIDEO_CAPTURE) // PRIV
+ val privUseCase2 = createUseCase(UseCaseConfigFactory.CaptureType.PREVIEW) // PRIV
+ val jpegUseCase = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_CAPTURE) // JPEG
+ val rawUseCase = createRawUseCase() // RAW
+ val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+ put(privUseCase1, previewSize)
+ put(privUseCase2, RESOLUTION_VGA)
+ put(jpegUseCase, maximumSize)
+ put(rawUseCase, maximumSize)
+ }
+ getSuggestedSpecsAndVerify(
+ useCaseExpectedResultMap,
+ hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_3
+ )
+ }
+
+ /**
+ * Unsupported PRIV + YUV + YUV + RAW for level-3 devices
+ */
+ @Test
+ fun throwsException_unsupportedConfiguration_inLevel3Device() {
+ val privUseCase = createUseCase(UseCaseConfigFactory.CaptureType.PREVIEW) // PRIV
+ val yuvUseCase1 = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_ANALYSIS) // YUV
+ val yuvUseCase2 = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_ANALYSIS) // YUV
+ val rawUseCase = createRawUseCase() // RAW
+ val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+ put(privUseCase, RESOLUTION_VGA)
+ put(yuvUseCase1, RESOLUTION_VGA)
+ put(yuvUseCase2, RESOLUTION_VGA)
+ put(rawUseCase, RESOLUTION_VGA)
+ }
+ Assert.assertThrows(IllegalArgumentException::class.java) {
+ getSuggestedSpecsAndVerify(
+ useCaseExpectedResultMap,
+ hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_3
+ )
+ }
+ }
+
+ // //////////////////////////////////////////////////////////////////////////////////////////
+ //
+ // Resolution selection tests for Burst-capability guaranteed configurations
+ //
+ // //////////////////////////////////////////////////////////////////////////////////////////
+
+ /**
+ * PRIV/PREVIEW + PRIV/MAXIMUM
+ */
+ @Test
+ fun canSelectCorrectSizes_privPlusPriv_inLimitedDevice_withBurstCapability() {
+ val privUseCase1 = createUseCase(UseCaseConfigFactory.CaptureType.VIDEO_CAPTURE) // PRIV
+ val privUseCase2 = createUseCase(UseCaseConfigFactory.CaptureType.PREVIEW) // PRIV
+ val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+ put(privUseCase1, maximumSize)
+ put(privUseCase2, previewSize)
+ }
+ getSuggestedSpecsAndVerify(
+ useCaseExpectedResultMap,
+ hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
+ capabilities = intArrayOf(
+ CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE
+ )
+ )
+ }
+
+ /**
+ * PRIV/PREVIEW + YUV/MAXIMUM
+ */
+ @Test
+ fun canSelectCorrectSizes_privPlusYuv_inLimitedDevice_withBurstCapability() {
+ val privUseCase = createUseCase(UseCaseConfigFactory.CaptureType.PREVIEW) // PRIV
+ val yuvUseCase = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_ANALYSIS) // YUV
+ val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+ put(privUseCase, previewSize)
+ put(yuvUseCase, maximumSize)
+ }
+ getSuggestedSpecsAndVerify(
+ useCaseExpectedResultMap,
+ hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
+ capabilities = intArrayOf(
+ CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE
+ )
+ )
+ }
+
+ /**
+ * YUV/PREVIEW + YUV/MAXIMUM
+ */
+ @Test
+ fun canSelectCorrectSizes_yuvPlusYuv_inLimitedDevice_withBurstCapability() {
+ val yuvUseCase1 = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_ANALYSIS) // YUV
+ val yuvUseCase2 = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_ANALYSIS) // YUV
+ val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+ put(yuvUseCase1, maximumSize)
+ put(yuvUseCase2, previewSize)
+ }
+ getSuggestedSpecsAndVerify(
+ useCaseExpectedResultMap,
+ hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
+ capabilities = intArrayOf(
+ CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE
+ )
+ )
+ }
+
+ // //////////////////////////////////////////////////////////////////////////////////////////
+ //
+ // Resolution selection tests for RAW-capability guaranteed configurations
+ //
+ // //////////////////////////////////////////////////////////////////////////////////////////
+
+ /**
+ * RAW/MAX
+ */
+ @Test
+ fun canSelectCorrectSizes_singleRawStream_inLimitedDevice_withRawCapability() {
+ val rawUseCase = createRawUseCase() // RAW
+ val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+ put(rawUseCase, maximumSize)
+ }
+ getSuggestedSpecsAndVerify(
+ useCaseExpectedResultMap,
+ hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
+ capabilities = intArrayOf(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW)
+ )
+ }
+
+ /**
+ * PRIV/PREVIEW + RAW/MAXIMUM
+ */
+ @Test
+ fun canSelectCorrectSizes_privPlusRAW_inLimitedDevice_withRawCapability() {
+ val privUseCase = createUseCase(UseCaseConfigFactory.CaptureType.PREVIEW) // PRIV
+ val rawUseCase = createRawUseCase() // RAW
+ val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+ put(privUseCase, previewSize)
+ put(rawUseCase, maximumSize)
+ }
+ getSuggestedSpecsAndVerify(
+ useCaseExpectedResultMap,
+ hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
+ capabilities = intArrayOf(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW)
+ )
+ }
+
+ /**
+ * PRIV/PREVIEW + PRIV/PREVIEW + RAW/MAXIMUM
+ */
+ @Test
+ fun canSelectCorrectSizes_privPlusPrivPlusRAW_inLimitedDevice_withRawCapability() {
+ val privUseCase1 = createUseCase(UseCaseConfigFactory.CaptureType.VIDEO_CAPTURE) // PRIV
+ val privUseCase2 = createUseCase(UseCaseConfigFactory.CaptureType.PREVIEW) // PRIV
+ val rawUseCase = createRawUseCase() // RAW
+ val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+ put(privUseCase1, previewSize)
+ put(privUseCase2, previewSize)
+ put(rawUseCase, maximumSize)
+ }
+ getSuggestedSpecsAndVerify(
+ useCaseExpectedResultMap,
+ hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
+ capabilities = intArrayOf(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW)
+ )
+ }
+
+ /**
+ * PRIV/PREVIEW + YUV/PREVIEW + RAW/MAXIMUM
+ */
+ @Test
+ fun canSelectCorrectSizes_privPlusYuvPlusRAW_inLimitedDevice_withRawCapability() {
+ val privUseCase = createUseCase(UseCaseConfigFactory.CaptureType.PREVIEW) // PRIV
+ val yuvUseCase = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_ANALYSIS) // YUV
+ val rawUseCase = createRawUseCase() // RAW
+ val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+ put(privUseCase, previewSize)
+ put(yuvUseCase, previewSize)
+ put(rawUseCase, maximumSize)
+ }
+ getSuggestedSpecsAndVerify(
+ useCaseExpectedResultMap,
+ hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
+ capabilities = intArrayOf(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW)
+ )
+ }
+
+ /**
+ * YUV/PREVIEW + YUV/PREVIEW + RAW/MAXIMUM
+ */
+ @Test
+ fun canSelectCorrectSizes_yuvPlusYuvPlusRAW_inLimitedDevice_withRawCapability() {
+ val yuvUseCase1 = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_ANALYSIS) // YUV
+ val yuvUseCase2 = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_ANALYSIS) // YUV
+ val rawUseCase = createRawUseCase() // RAW
+ val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+ put(yuvUseCase1, previewSize)
+ put(yuvUseCase2, previewSize)
+ put(rawUseCase, maximumSize)
+ }
+ getSuggestedSpecsAndVerify(
+ useCaseExpectedResultMap,
+ hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
+ capabilities = intArrayOf(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW)
+ )
+ }
+
+ /**
+ * PRIV/PREVIEW + JPEG/MAXIMUM + RAW/MAXIMUM
+ */
+ @Test
+ fun canSelectCorrectSizes_privPlusJpegPlusRAW_inLimitedDevice_withRawCapability() {
+ val privUseCase = createUseCase(UseCaseConfigFactory.CaptureType.PREVIEW) // PRIV
+ val jpegUseCase = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_CAPTURE) // JPEG
+ val rawUseCase = createRawUseCase() // RAW
+ val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+ put(privUseCase, previewSize)
+ put(jpegUseCase, maximumSize)
+ put(rawUseCase, maximumSize)
+ }
+ getSuggestedSpecsAndVerify(
+ useCaseExpectedResultMap,
+ hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
+ capabilities = intArrayOf(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW)
+ )
+ }
+
+ /**
+ * YUV/PREVIEW + JPEG/MAXIMUM + RAW/MAXIMUM
+ */
+ @Test
+ fun canSelectCorrectSizes_yuvPlusJpegPlusRAW_inLimitedDevice_withRawCapability() {
+ val yuvUseCase = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_ANALYSIS) // YUV
+ val jpegUseCase = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_CAPTURE) // JPEG
+ val rawUseCase = createRawUseCase() // RAW
+ val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+ put(yuvUseCase, previewSize)
+ put(jpegUseCase, maximumSize)
+ put(rawUseCase, maximumSize)
+ }
+ getSuggestedSpecsAndVerify(
+ useCaseExpectedResultMap,
+ hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
+ capabilities = intArrayOf(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW)
+ )
+ }
+
+ private fun getSuggestedSpecsAndVerify(
+ useCasesExpectedResultMap: Map<UseCase, Size>,
+ attachedSurfaceInfoList: List<AttachedSurfaceInfo> = emptyList(),
+ hardwareLevel: Int = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY,
+ capabilities: IntArray? = null,
+ compareWithAtMost: Boolean = false
+ ) {
+ setupCamera(hardwareLevel = hardwareLevel, capabilities = capabilities)
+ val supportedSurfaceCombination = SupportedSurfaceCombination(
+ context, fakeCameraMetadata,
+ mockEncoderProfilesAdapter
+ )
+
+ val useCaseConfigMap = getUseCaseToConfigMap(useCasesExpectedResultMap.keys.toList())
+ val useCaseConfigToOutputSizesMap =
+ getUseCaseConfigToOutputSizesMap(useCaseConfigMap.values.toList())
+ val suggestedStreamSpecs = supportedSurfaceCombination.getSuggestedStreamSpecifications(
+ false,
+ attachedSurfaceInfoList,
+ useCaseConfigToOutputSizesMap
+ )
+
+ useCasesExpectedResultMap.keys.forEach {
+ val resultSize = suggestedStreamSpecs[useCaseConfigMap[it]]!!.resolution
+ val expectedSize = useCasesExpectedResultMap[it]!!
+ if (!compareWithAtMost) {
+ assertThat(resultSize).isEqualTo(expectedSize)
+ } else {
+ assertThat(sizeIsAtMost(resultSize, expectedSize)).isTrue()
+ }
+ }
+ }
+
+ private fun getUseCaseToConfigMap(useCases: List<UseCase>): Map<UseCase, UseCaseConfig<*>> {
+ val useCaseConfigMap = mutableMapOf<UseCase, UseCaseConfig<*>>().apply {
+ useCases.forEach {
+ put(it, it.currentConfig)
+ }
+ }
+ return useCaseConfigMap
+ }
+
+ private fun getUseCaseConfigToOutputSizesMap(
+ useCaseConfigs: List<UseCaseConfig<*>>
+ ): Map<UseCaseConfig<*>, List<Size>> {
+ val resultMap = mutableMapOf<UseCaseConfig<*>, List<Size>>().apply {
+ useCaseConfigs.forEach {
+ put(it, supportedSizes.toList())
+ }
+ }
+
+ return resultMap
+ }
+
+ /**
+ * Helper function that returns whether size is <= maxSize
+ *
+ */
+ private fun sizeIsAtMost(size: Size, maxSize: Size): Boolean {
+ return (size.height * size.width) <= (maxSize.height * maxSize.width)
+ }
+
+ // //////////////////////////////////////////////////////////////////////////////////////////
+ //
+ // Other tests
+ //
+ // //////////////////////////////////////////////////////////////////////////////////////////
+
@Test
fun maximumSizeForImageFormat() {
setupCamera(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY)
@@ -1380,981 +1501,6 @@
}
@Test
- fun isAspectRatioMatchWithSupportedMod16Resolution() {
- setupCamera(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED)
- val supportedSurfaceCombination = SupportedSurfaceCombination(
- context, fakeCameraMetadata,
- mockEncoderProfilesAdapter
- )
- val preview = Preview.Builder()
- .setTargetAspectRatio(AspectRatio.RATIO_16_9)
- .setDefaultResolution(mod16Size)
- .build()
- val imageCapture = ImageCapture.Builder()
- .setTargetAspectRatio(AspectRatio.RATIO_16_9)
- .setDefaultResolution(mod16Size)
- .build()
- val useCases: MutableList<UseCase> = ArrayList()
- useCases.add(preview)
- useCases.add(imageCapture)
- val useCaseToConfigMap = Configs.useCaseConfigMapWithDefaultSettingsFromUseCaseList(
- cameraFactory!!.getCamera(fakeCameraMetadata.camera.value).cameraInfoInternal,
- useCases,
- useCaseConfigFactory
- )
- val suggestedStreamSpecMap: Map<UseCaseConfig<*>, StreamSpec> =
- supportedSurfaceCombination.getSuggestedStreamSpecifications(
- false,
- emptyList(),
- ArrayList(useCaseToConfigMap.values)
- )
- assertThat(suggestedStreamSpecMap).containsEntry(
- useCaseToConfigMap[preview],
- mod16SizeStreamSpec
- )
- assertThat(suggestedStreamSpecMap).containsEntry(
- useCaseToConfigMap[imageCapture],
- mod16SizeStreamSpec
- )
- }
-
- @Test
- fun sortByCompareSizesByArea_canSortSizesCorrectly() {
- val sizes = arrayOfNulls<Size>(supportedSizes.size)
-
- // Generates a unsorted array from mSupportedSizes.
- val centerIndex = supportedSizes.size / 2
- // Puts 2nd half sizes in the front
- if (supportedSizes.size - centerIndex >= 0) {
- System.arraycopy(
- supportedSizes,
- centerIndex, sizes, 0,
- supportedSizes.size - centerIndex
- )
- }
- // Puts 1st half sizes inversely in the tail
- for (j in centerIndex - 1 downTo 0) {
- sizes[supportedSizes.size - j - 1] = supportedSizes[j]
- }
-
- // The testing sizes array will be equal to mSupportedSizes after sorting.
- Arrays.sort(sizes, CompareSizesByArea(true))
- assertThat(listOf(*sizes)).isEqualTo(listOf(*supportedSizes))
- }
-
- @Test
- fun supportedOutputSizes_noConfigSettings() {
- setupCamera(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED)
- val supportedSurfaceCombination = SupportedSurfaceCombination(
- context, fakeCameraMetadata,
- mockEncoderProfilesAdapter
- )
- val useCase = FakeUseCaseConfig.Builder().build()
-
- // 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: List<Size?> = supportedSurfaceCombination.getSupportedOutputSizes(
- useCase.currentConfig
- )
- 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 supportedOutputSizes_aspectRatio4x3() {
- setupCamera(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED)
- val supportedSurfaceCombination = SupportedSurfaceCombination(
- context, fakeCameraMetadata,
- mockEncoderProfilesAdapter
- )
- val useCase = FakeUseCaseConfig.Builder().setTargetAspectRatio(AspectRatio.RATIO_4_3)
- .build()
-
- // 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: List<Size?> = supportedSurfaceCombination.getSupportedOutputSizes(
- useCase.currentConfig
- )
- 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 supportedOutputSizes_aspectRatio16x9() {
- setupCamera(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED)
- val supportedSurfaceCombination = SupportedSurfaceCombination(
- context, fakeCameraMetadata,
- mockEncoderProfilesAdapter
- )
- val useCase = FakeUseCaseConfig.Builder().setTargetAspectRatio(
- AspectRatio.RATIO_16_9
- ).build()
-
- // 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: List<Size?> = supportedSurfaceCombination.getSupportedOutputSizes(
- useCase.currentConfig
- )
- 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 supportedOutputSizes_targetResolution1080x1920InRotation0() {
- setupCamera(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED)
- val supportedSurfaceCombination = SupportedSurfaceCombination(
- context, fakeCameraMetadata,
- mockEncoderProfilesAdapter
- )
- val useCase = FakeUseCaseConfig.Builder().setTargetResolution(
- Size(1080, 1920)
- ).build()
-
- // 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: List<Size?> = supportedSurfaceCombination.getSupportedOutputSizes(
- useCase.currentConfig
- )
- 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 supportedOutputSizes_targetResolutionLargerThan640x480() {
- setupCamera(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED)
- val supportedSurfaceCombination = SupportedSurfaceCombination(
- context, fakeCameraMetadata,
- mockEncoderProfilesAdapter
- )
- val useCase = FakeUseCaseConfig.Builder().setTargetRotation(
- Surface.ROTATION_90
- ).setTargetResolution(Size(1280, 960)).build()
-
- // 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: List<Size?> = supportedSurfaceCombination.getSupportedOutputSizes(
- useCase.currentConfig
- )
- 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 supportedOutputSizes_targetResolutionSmallerThan640x480() {
- setupCamera(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED)
- val supportedSurfaceCombination = SupportedSurfaceCombination(
- context, fakeCameraMetadata,
- mockEncoderProfilesAdapter
- )
- val useCase = FakeUseCaseConfig.Builder().setTargetRotation(
- Surface.ROTATION_90
- ).setTargetResolution(Size(320, 240)).build()
-
- // 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: List<Size?> = supportedSurfaceCombination.getSupportedOutputSizes(
- useCase.currentConfig
- )
- 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 supportedOutputSizes_targetResolution1800x1440NearTo4x3() {
- setupCamera(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED)
- val supportedSurfaceCombination = SupportedSurfaceCombination(
- context, fakeCameraMetadata,
- mockEncoderProfilesAdapter
- )
- val useCase = FakeUseCaseConfig.Builder().setTargetRotation(
- Surface.ROTATION_90
- ).setTargetResolution(Size(1800, 1440)).build()
-
- // 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: List<Size?> = supportedSurfaceCombination.getSupportedOutputSizes(
- useCase.currentConfig
- )
- 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 supportedOutputSizes_targetResolution1280x600NearTo16x9() {
- setupCamera(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED)
- val supportedSurfaceCombination = SupportedSurfaceCombination(
- context, fakeCameraMetadata,
- mockEncoderProfilesAdapter
- )
- val useCase = FakeUseCaseConfig.Builder().setTargetResolution(
- Size(1280, 600)
- ).setTargetRotation(Surface.ROTATION_90).build()
-
- // 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: List<Size?> = supportedSurfaceCombination.getSupportedOutputSizes(
- useCase.currentConfig
- )
- 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 supportedOutputSizes_maxResolution1280x720() {
- setupCamera(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED)
- val supportedSurfaceCombination = SupportedSurfaceCombination(
- context, fakeCameraMetadata,
- mockEncoderProfilesAdapter
- )
- val useCase = FakeUseCaseConfig.Builder().setMaxResolution(Size(1280, 720)).build()
-
- // 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: List<Size?> = supportedSurfaceCombination.getSupportedOutputSizes(
- useCase.currentConfig
- )
- val expectedList = listOf(
- Size(1280, 720),
- Size(960, 544),
- Size(800, 450),
- Size(640, 480)
- )
- assertThat(resultList).isEqualTo(expectedList)
- }
-
- @Test
- fun supportedOutputSizes_setCustomOrderedResolutions() {
- setupCamera(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED)
- val supportedSurfaceCombination = SupportedSurfaceCombination(
- context, fakeCameraMetadata,
- mockEncoderProfilesAdapter
- )
- val customOrderedResolutions = listOf(
- Size(640, 480),
- Size(1280, 720),
- Size(1920, 1080),
- Size(3840, 2160),
- )
- val useCase = FakeUseCaseConfig.Builder()
- .setCustomOrderedResolutions(customOrderedResolutions)
- .setTargetResolution(Size(1280, 720))
- .setMaxResolution(Size(1920, 1440))
- .setDefaultResolution(Size(1280, 720))
- .setSupportedResolutions(
- listOf(
- Pair.create(
- ImageFormat.PRIVATE, arrayOf(
- Size(800, 450),
- Size(640, 480),
- Size(320, 240),
- )
- )
- )
- ).build()
-
- // Custom ordered resolutions is fully respected, meaning it will not be sorted or filtered
- // by other configurations such as max/default/target/supported resolutions.
- val resultList: List<Size?> = supportedSurfaceCombination.getSupportedOutputSizes(
- useCase.currentConfig
- )
- assertThat(resultList).containsExactlyElementsIn(customOrderedResolutions).inOrder()
- }
-
- @Test
- fun supportedOutputSizes_defaultResolution1280x720_noTargetResolution() {
- setupCamera(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED)
- val supportedSurfaceCombination = SupportedSurfaceCombination(
- context, fakeCameraMetadata,
- mockEncoderProfilesAdapter
- )
- val useCase = FakeUseCaseConfig.Builder().setDefaultResolution(
- Size(
- 1280,
- 720
- )
- ).build()
-
- // 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: List<Size?> = supportedSurfaceCombination.getSupportedOutputSizes(
- useCase.currentConfig
- )
- val expectedList = listOf(
- Size(1280, 720),
- Size(960, 544),
- Size(800, 450),
- Size(640, 480)
- )
- assertThat(resultList).isEqualTo(expectedList)
- }
-
- @Test
- fun supportedOutputSizes_defaultResolution1280x720_targetResolution1920x1080() {
- setupCamera(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED)
- val supportedSurfaceCombination = SupportedSurfaceCombination(
- context, fakeCameraMetadata,
- mockEncoderProfilesAdapter
- )
- val useCase = FakeUseCaseConfig.Builder().setDefaultResolution(
- Size(1280, 720)
- ).setTargetRotation(Surface.ROTATION_90).setTargetResolution(
- Size(1920, 1080)
- ).build()
-
- // 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: List<Size?> = supportedSurfaceCombination.getSupportedOutputSizes(
- useCase.currentConfig
- )
- 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 supportedOutputSizes_fallbackToGuaranteedResolution_whenNotFulfillConditions() {
- setupCamera(
- CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED, arrayOf(
- Size(640, 480),
- Size(320, 240),
- Size(320, 180),
- Size(256, 144)
- )
- )
- val supportedSurfaceCombination = SupportedSurfaceCombination(
- context, fakeCameraMetadata,
- mockEncoderProfilesAdapter
- )
- val useCase = FakeUseCaseConfig.Builder().setTargetResolution(
- Size(1920, 1080)
- ).setTargetRotation(Surface.ROTATION_90).build()
-
- // 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: List<Size?> = supportedSurfaceCombination.getSupportedOutputSizes(
- useCase.currentConfig
- )
- val expectedList = listOf(Size(640, 480))
- assertThat(resultList).isEqualTo(expectedList)
- }
-
- @Test
- fun supportedOutputSizes_whenMaxSizeSmallerThanDefaultMiniSize() {
- setupCamera(
- CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED, arrayOf(
- Size(640, 480),
- Size(320, 240),
- Size(320, 180),
- Size(256, 144)
- )
- )
- val supportedSurfaceCombination = SupportedSurfaceCombination(
- context, fakeCameraMetadata,
- mockEncoderProfilesAdapter
- )
- val useCase = FakeUseCaseConfig.Builder().setMaxResolution(
- Size(320, 240)
- ).build()
-
- // 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: List<Size?> = supportedSurfaceCombination.getSupportedOutputSizes(
- useCase.currentConfig
- )
- val expectedList = listOf(
- Size(320, 240),
- Size(320, 180),
- Size(256, 144)
- )
- assertThat(resultList).isEqualTo(expectedList)
- }
-
- @Test
- fun supportedOutputSizes_whenMaxSizeSmallerThanSmallTargetResolution() {
- setupCamera(
- CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED, arrayOf(
- Size(640, 480),
- Size(320, 240),
- Size(320, 180),
- Size(256, 144)
- )
- )
- val supportedSurfaceCombination = SupportedSurfaceCombination(
- context, fakeCameraMetadata,
- mockEncoderProfilesAdapter
- )
- val useCase = FakeUseCaseConfig.Builder().setMaxResolution(
- Size(320, 180)
- ).setTargetResolution(Size(320, 240)).setTargetRotation(
- Surface.ROTATION_90
- ).build()
-
- // 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: List<Size?> = supportedSurfaceCombination.getSupportedOutputSizes(
- useCase.currentConfig
- )
- val expectedList = listOf(
- Size(320, 180),
- Size(256, 144)
- )
- assertThat(resultList).isEqualTo(expectedList)
- }
-
- @Test
- fun supportedOutputSizes_whenBothMaxAndTargetResolutionsSmallerThan640x480() {
- setupCamera(
- CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED, arrayOf(
- Size(640, 480),
- Size(320, 240),
- Size(320, 180),
- Size(256, 144)
- )
- )
- val supportedSurfaceCombination = SupportedSurfaceCombination(
- context, fakeCameraMetadata,
- mockEncoderProfilesAdapter
- )
- val useCase = FakeUseCaseConfig.Builder().setMaxResolution(
- Size(320, 240)
- ).setTargetResolution(Size(320, 180)).setTargetRotation(
- Surface.ROTATION_90
- ).build()
-
- // 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: List<Size?> = supportedSurfaceCombination.getSupportedOutputSizes(
- useCase.currentConfig
- )
- val expectedList = listOf(
- Size(320, 180),
- Size(256, 144),
- Size(320, 240)
- )
- assertThat(resultList).isEqualTo(expectedList)
- }
-
- @Test
- fun supportedOutputSizes_whenMaxSizeSmallerThanBigTargetResolution() {
- setupCamera(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED)
- val supportedSurfaceCombination = SupportedSurfaceCombination(
- context, fakeCameraMetadata,
- mockEncoderProfilesAdapter
- )
- val useCase = FakeUseCaseConfig.Builder().setMaxResolution(
- Size(1920, 1080)
- ).setTargetResolution(Size(3840, 2160)).setTargetRotation(
- Surface.ROTATION_90
- ).build()
-
- // 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: List<Size?> = supportedSurfaceCombination.getSupportedOutputSizes(
- useCase.currentConfig
- )
- 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 supportedOutputSizes_whenNoSizeBetweenMaxSizeAndTargetResolution() {
- setupCamera(
- CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED, arrayOf(
- Size(640, 480),
- Size(320, 240),
- Size(320, 180),
- Size(256, 144)
- )
- )
- val supportedSurfaceCombination = SupportedSurfaceCombination(
- context, fakeCameraMetadata,
- mockEncoderProfilesAdapter
- )
- val useCase = FakeUseCaseConfig.Builder().setMaxResolution(
- Size(320, 200)
- ).setTargetResolution(Size(320, 190)).setTargetRotation(
- Surface.ROTATION_90
- ).build()
-
- // 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: List<Size?> = supportedSurfaceCombination.getSupportedOutputSizes(
- useCase.currentConfig
- )
- val expectedList = listOf(
- Size(320, 180),
- Size(256, 144)
- )
- assertThat(resultList).isEqualTo(expectedList)
- }
-
- @Test
- fun supportedOutputSizes_whenTargetResolutionSmallerThanAnySize() {
- setupCamera(
- CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED, arrayOf(
- Size(640, 480),
- Size(320, 240),
- Size(320, 180),
- Size(256, 144)
- )
- )
- val supportedSurfaceCombination = SupportedSurfaceCombination(
- context, fakeCameraMetadata,
- mockEncoderProfilesAdapter
- )
- val useCase = FakeUseCaseConfig.Builder().setTargetResolution(
- Size(192, 144)
- ).setTargetRotation(Surface.ROTATION_90).build()
-
- // 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: List<Size?> = supportedSurfaceCombination.getSupportedOutputSizes(
- useCase.currentConfig
- )
- val expectedList = listOf(
- Size(320, 240),
- Size(256, 144)
- )
- assertThat(resultList).isEqualTo(expectedList)
- }
-
- @Test
- fun supportedOutputSizes_whenMaxResolutionSmallerThanAnySize() {
- setupCamera(
- CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY, arrayOf(
- Size(640, 480),
- Size(320, 240),
- Size(320, 180),
- Size(256, 144)
- )
- )
- val supportedSurfaceCombination = SupportedSurfaceCombination(
- context, fakeCameraMetadata,
- mockEncoderProfilesAdapter
- )
- val useCase = FakeUseCaseConfig.Builder().setMaxResolution(
- Size(192, 144)
- ).build()
-
- // All sizes will be filtered out by the max resolution 192x144 setting and an
- // IllegalArgumentException will be thrown.
- assertThrows(IllegalArgumentException::class.java) {
- supportedSurfaceCombination.getSupportedOutputSizes(useCase.currentConfig)
- }
- }
-
- @Test
- fun supportedOutputSizes_whenMod16IsIgnoredForSmallSizes() {
- setupCamera(
- CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED, arrayOf(
- Size(640, 480),
- Size(320, 240),
- Size(320, 180),
- Size(296, 144),
- Size(256, 144)
- )
- )
- val supportedSurfaceCombination = SupportedSurfaceCombination(
- context, fakeCameraMetadata,
- mockEncoderProfilesAdapter
- )
- val useCase = FakeUseCaseConfig.Builder().setTargetResolution(
- Size(185, 90)
- ).setTargetRotation(Surface.ROTATION_90).build()
-
- // 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: List<Size?> = supportedSurfaceCombination.getSupportedOutputSizes(
- useCase.currentConfig
- )
- val expectedList = listOf(
- Size(296, 144),
- Size(256, 144),
- Size(320, 240)
- )
- assertThat(resultList).isEqualTo(expectedList)
- }
-
- @Test
- fun supportedOutputSizes_whenOneMod16SizeClosestToTargetResolution() {
- setupCamera(
- CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY, 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, fakeCameraMetadata,
- mockEncoderProfilesAdapter
- )
- val useCase = FakeUseCaseConfig.Builder().setTargetResolution(
- Size(1080, 2016)
- ).build()
- val resultList: List<Size?> = supportedSurfaceCombination.getSupportedOutputSizes(
- useCase.currentConfig
- )
- 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 supportedOutputSizesWithPortraitPixelArraySize_aspectRatio16x9() {
- val 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)
- )
-
- // 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.
- setupCamera(
- CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
- sensorOrientation0, portraitPixelArraySize, supportedSizes, null
- )
- val supportedSurfaceCombination = SupportedSurfaceCombination(
- context, fakeCameraMetadata,
- mockEncoderProfilesAdapter
- )
- val useCase = FakeUseCaseConfig.Builder().setTargetAspectRatio(
- AspectRatio.RATIO_16_9
- ).build()
-
- // 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: List<Size?> = supportedSurfaceCombination.getSupportedOutputSizes(
- useCase.currentConfig
- )
- 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 supportedOutputSizesOnTabletWithPortraitPixelArraySize_aspectRatio16x9() {
- val 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)
- )
-
- // 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.
- setupCamera(
- CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
- sensorOrientation90, portraitPixelArraySize, supportedSizes, null
- )
- val supportedSurfaceCombination = SupportedSurfaceCombination(
- context, fakeCameraMetadata,
- mockEncoderProfilesAdapter
- )
- val useCase = FakeUseCaseConfig.Builder().setTargetAspectRatio(
- AspectRatio.RATIO_16_9
- ).build()
-
- // 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: List<Size?> = supportedSurfaceCombination.getSupportedOutputSizes(
- useCase.currentConfig
- )
- 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 supportedOutputSizesOnTablet_aspectRatio16x9() {
- setupCamera(
- CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
- sensorOrientation0, landscapePixelArraySize, supportedSizes, null
- )
- val supportedSurfaceCombination = SupportedSurfaceCombination(
- context, fakeCameraMetadata,
- mockEncoderProfilesAdapter
- )
- val useCase = FakeUseCaseConfig.Builder().setTargetAspectRatio(
- AspectRatio.RATIO_16_9
- ).build()
-
- // 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: List<Size?> = supportedSurfaceCombination.getSupportedOutputSizes(
- useCase.currentConfig
- )
- 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 supportedOutputSizesOnTabletWithPortraitSizes_aspectRatio16x9() {
- val 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)
- )
- setupCamera(
- CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
- sensorOrientation0, landscapePixelArraySize, supportedSizes, null
- )
- val supportedSurfaceCombination = SupportedSurfaceCombination(
- context, fakeCameraMetadata,
- mockEncoderProfilesAdapter
- )
- val useCase = FakeUseCaseConfig.Builder().setTargetAspectRatio(
- AspectRatio.RATIO_16_9
- ).build()
-
- // 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: List<Size?> = supportedSurfaceCombination.getSupportedOutputSizes(
- useCase.currentConfig
- )
- 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
setupCamera(
@@ -2374,173 +1520,6 @@
)
}
- @Test
- fun canGet640x480_whenAnotherGroupMatchedInMod16Exists() {
- val 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)
- )
- setupCamera(
- CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
- sensorOrientation90, landscapePixelArraySize, supportedSizes, null
- )
- val supportedSurfaceCombination = SupportedSurfaceCombination(
- context, fakeCameraMetadata,
- mockEncoderProfilesAdapter
- )
-
- // Sets the target resolution as 640x480 with target rotation as ROTATION_90 because the
- // sensor orientation is 90.
- val useCase = FakeUseCaseConfig.Builder().setTargetResolution(
- vgaSize
- ).setTargetRotation(Surface.ROTATION_90).build()
- val suggestedStreamSpecMap = supportedSurfaceCombination.getSuggestedStreamSpecifications(
- false,
- emptyList(),
- listOf(useCase.currentConfig)
- )
-
- // Checks 640x480 is final selected for the use case.
- assertThat(suggestedStreamSpecMap[useCase.currentConfig]?.resolution).isEqualTo(vgaSize)
- }
-
- @Test
- fun canGetSupportedSizeSmallerThan640x480_whenLargerMaxResolutionIsSet() {
- val supportedSizes = arrayOf(
- Size(480, 480)
- )
- setupCamera(
- CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
- sensorOrientation90, landscapePixelArraySize, supportedSizes, null
- )
- val supportedSurfaceCombination = SupportedSurfaceCombination(
- context, fakeCameraMetadata,
- mockEncoderProfilesAdapter
- )
-
- // Sets the max resolution as 720x1280
- val useCase = FakeUseCaseConfig.Builder().setMaxResolution(displaySize).build()
- val suggestedStreamSpecMap = supportedSurfaceCombination.getSuggestedStreamSpecifications(
- false,
- emptyList(),
- listOf(useCase.currentConfig)
- )
-
- // Checks 480x480 is final selected for the use case.
- assertThat(suggestedStreamSpecMap[useCase.currentConfig]?.resolution).isEqualTo(
- Size(480, 480)
- )
- }
-
- @Test
- fun previewSizeIsSelectedForImageAnalysis_imageCaptureHasNoSetSizeInLimitedDevice() {
- setupCamera(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED)
- val supportedSurfaceCombination = SupportedSurfaceCombination(
- context, fakeCameraMetadata,
- mockEncoderProfilesAdapter
- )
- val preview = Preview.Builder().build()
- preview.setSurfaceProvider(
- CameraXExecutors.directExecutor(),
- SurfaceTextureProvider.createSurfaceTextureProvider(
- mock()
- )
- )
-
- // ImageCapture has no explicit target resolution setting
- val imageCapture = ImageCapture.Builder().build()
-
- // 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 = ImageAnalysis.Builder()
- .setTargetRotation(Surface.ROTATION_90)
- .setTargetResolution(recordSize)
- .build()
- val useCases: MutableList<UseCase> = ArrayList()
- useCases.add(preview)
- useCases.add(imageCapture)
- useCases.add(imageAnalysis)
- val useCaseToConfigMap = Configs.useCaseConfigMapWithDefaultSettingsFromUseCaseList(
- cameraFactory!!.getCamera(fakeCameraMetadata.camera.value).cameraInfoInternal,
- useCases,
- useCaseConfigFactory
- )
- val suggestedStreamSpecMap = supportedSurfaceCombination.getSuggestedStreamSpecifications(
- false,
- emptyList(),
- ArrayList(useCaseToConfigMap.values)
- )
- assertThat(suggestedStreamSpecMap[useCaseToConfigMap[imageAnalysis]]?.resolution).isEqualTo(
- previewSize
- )
- }
-
- @Test
- fun recordSizeIsSelectedForImageAnalysis_imageCaptureHasExplicitSizeInLimitedDevice() {
- setupCamera(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED)
- val supportedSurfaceCombination = SupportedSurfaceCombination(
- context, fakeCameraMetadata,
- mockEncoderProfilesAdapter
- )
- val preview = Preview.Builder().build()
- preview.setSurfaceProvider(
- CameraXExecutors.directExecutor(),
- SurfaceTextureProvider.createSurfaceTextureProvider(
- mock()
- )
- )
-
- // ImageCapture has no explicit RECORD size target resolution setting
- val imageCapture = ImageCapture.Builder()
- .setTargetRotation(Surface.ROTATION_90)
- .setTargetResolution(recordSize)
- .build()
-
- // 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 = ImageAnalysis.Builder()
- .setTargetRotation(Surface.ROTATION_90)
- .setTargetResolution(recordSize)
- .build()
- val useCases: MutableList<UseCase> = ArrayList()
- useCases.add(preview)
- useCases.add(imageCapture)
- useCases.add(imageAnalysis)
- val useCaseToConfigMap = Configs.useCaseConfigMapWithDefaultSettingsFromUseCaseList(
- cameraFactory!!.getCamera(fakeCameraMetadata.camera.value).cameraInfoInternal,
- useCases,
- useCaseConfigFactory
- )
- val suggestedStreamSpecMap = supportedSurfaceCombination.getSuggestedStreamSpecifications(
- false,
- emptyList(),
- ArrayList(useCaseToConfigMap.values)
- )
- assertThat(suggestedStreamSpecMap[useCaseToConfigMap[imageAnalysis]]?.resolution).isEqualTo(
- recordSize
- )
- }
-
private fun setupCamera(hardwareLevel: Int, capabilities: IntArray) {
setupCamera(
hardwareLevel, sensorOrientation90, landscapePixelArraySize,
@@ -2696,43 +1675,27 @@
return true
}
- /** Creates a VideoCapture with one ore more specific Quality */
- private fun createVideoCapture(vararg quality: Quality): VideoCapture<TestVideoOutput> {
- return createVideoCapture(QualitySelector.fromOrderedList(listOf(*quality)))
- }
- /** Creates a VideoCapture with a customized QualitySelector */
- /** Creates a VideoCapture with a default QualitySelector */
- @JvmOverloads
- fun createVideoCapture(
- qualitySelector: QualitySelector = VideoSpec.QUALITY_SELECTOR_AUTO
- ): VideoCapture<TestVideoOutput> {
- val mediaSpecBuilder = MediaSpec.builder()
- mediaSpecBuilder.configureVideo { builder: VideoSpec.Builder ->
- builder.setQualitySelector(
- qualitySelector
- )
+ private fun createUseCase(
+ captureType: UseCaseConfigFactory.CaptureType,
+ targetFrameRate: Range<Int>? = null
+ ): UseCase {
+ val builder = FakeUseCaseConfig.Builder(captureType, when (captureType) {
+ UseCaseConfigFactory.CaptureType.IMAGE_CAPTURE -> ImageFormat.JPEG
+ UseCaseConfigFactory.CaptureType.IMAGE_ANALYSIS -> ImageFormat.YUV_420_888
+ else -> ImageFormatConstants.INTERNAL_DEFINED_IMAGE_FORMAT_PRIVATE
+ })
+ targetFrameRate?.let {
+ builder.mutableConfig.insertOption(UseCaseConfig.OPTION_TARGET_FRAME_RATE, it)
}
- val videoOutput = TestVideoOutput()
- videoOutput.mediaSpecObservable.setState(mediaSpecBuilder.build())
- return VideoCapture.withOutput(videoOutput)
+ return builder.build()
}
- /** A fake implementation of VideoOutput */
- class TestVideoOutput : VideoOutput {
- var mediaSpecObservable =
- MutableStateObservable.withInitialState(MediaSpec.builder().build())
- private var surfaceRequest: SurfaceRequest? = null
- private var sourceState: SourceState? = null
- override fun onSurfaceRequested(request: SurfaceRequest) {
- surfaceRequest = request
- }
-
- override fun getMediaSpec(): Observable<MediaSpec> {
- return mediaSpecObservable
- }
-
- override fun onSourceStateChanged(sourceState: SourceState) {
- this.sourceState = sourceState
- }
+ private fun createRawUseCase(): UseCase {
+ val builder = FakeUseCaseConfig.Builder()
+ builder.mutableConfig.insertOption(
+ UseCaseConfig.OPTION_INPUT_FORMAT,
+ ImageFormat.RAW_SENSOR
+ )
+ return builder.build()
}
}
\ No newline at end of file
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2DeviceSurfaceManager.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2DeviceSurfaceManager.java
index 5e8dd36..38a2e6a 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2DeviceSurfaceManager.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2DeviceSurfaceManager.java
@@ -17,6 +17,7 @@
package androidx.camera.camera2.internal;
import android.content.Context;
+import android.hardware.camera2.CameraDevice;
import android.media.CamcorderProfile;
import android.util.Size;
@@ -43,7 +44,7 @@
* Camera device manager to provide the guaranteed supported stream capabilities related info for
* all camera devices
*
- * <p>{@link android.hardware.camera2.CameraDevice#createCaptureSession} defines the default
+ * <p>{@link CameraDevice#createCaptureSession} defines the default
* guaranteed stream combinations for different hardware level devices. It defines what combination
* of surface configuration type and size pairs can be supported for different hardware level camera
* devices. This structure is used to store the guaranteed supported stream capabilities related
@@ -177,12 +178,16 @@
/**
* Retrieves a map of suggested stream specifications for the given list of use cases.
*
- * @param isConcurrentCameraModeOn true if concurrent camera mode is on, otherwise false.
- * @param cameraId the camera id of the camera device used by the use cases
- * @param existingSurfaces list of surfaces already configured and used by the camera. The
- * stream specifications for these surface can not change.
- * @param newUseCaseConfigs list of configurations of the use cases that will be given a
- * suggested stream specification
+ * @param isConcurrentCameraModeOn true if concurrent camera mode is on, otherwise
+ * false.
+ * @param cameraId the camera id of the camera device used by the
+ * use cases
+ * @param existingSurfaces list of surfaces already configured and used by
+ * the camera. The stream specifications for these
+ * surface can not change.
+ * @param newUseCaseConfigsSupportedSizeMap map of configurations of the use cases to the
+ * supported sizes list that will be given a
+ * suggested stream specification
* @return map of suggested stream specifications for given use cases
* @throws IllegalStateException if not initialized
* @throws IllegalArgumentException if {@code newUseCaseConfigs} is an empty list, if
@@ -193,11 +198,11 @@
@NonNull
@Override
public Map<UseCaseConfig<?>, StreamSpec> getSuggestedStreamSpecs(
- boolean isConcurrentCameraModeOn,
- @NonNull String cameraId,
+ boolean isConcurrentCameraModeOn, @NonNull String cameraId,
@NonNull List<AttachedSurfaceInfo> existingSurfaces,
- @NonNull List<UseCaseConfig<?>> newUseCaseConfigs) {
- Preconditions.checkArgument(!newUseCaseConfigs.isEmpty(), "No new use cases to be bound.");
+ @NonNull Map<UseCaseConfig<?>, List<Size>> newUseCaseConfigsSupportedSizeMap) {
+ Preconditions.checkArgument(!newUseCaseConfigsSupportedSizeMap.isEmpty(),
+ "No new use cases to be bound.");
SupportedSurfaceCombination supportedSurfaceCombination =
mCameraSupportedSurfaceCombinationMap.get(cameraId);
@@ -210,6 +215,6 @@
return supportedSurfaceCombination.getSuggestedStreamSpecifications(
isConcurrentCameraModeOn,
existingSurfaces,
- newUseCaseConfigs);
+ newUseCaseConfigsSupportedSizeMap);
}
}
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/SupportedOutputSizesCollector.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/SupportedOutputSizesCollector.java
deleted file mode 100644
index b0b6984..0000000
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/SupportedOutputSizesCollector.java
+++ /dev/null
@@ -1,590 +0,0 @@
-/*
- * 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 static androidx.camera.core.impl.utils.AspectRatioUtil.ASPECT_RATIO_16_9;
-import static androidx.camera.core.impl.utils.AspectRatioUtil.ASPECT_RATIO_3_4;
-import static androidx.camera.core.impl.utils.AspectRatioUtil.ASPECT_RATIO_4_3;
-import static androidx.camera.core.impl.utils.AspectRatioUtil.ASPECT_RATIO_9_16;
-import static androidx.camera.core.impl.utils.AspectRatioUtil.hasMatchingAspectRatio;
-
-import android.graphics.Rect;
-import android.hardware.camera2.CameraCharacteristics;
-import android.hardware.camera2.params.StreamConfigurationMap;
-import android.os.Build;
-import android.util.Rational;
-import android.util.Size;
-
-import androidx.annotation.DoNotInline;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.RequiresApi;
-import androidx.camera.camera2.internal.compat.CameraCharacteristicsCompat;
-import androidx.camera.camera2.internal.compat.StreamConfigurationMapCompat;
-import androidx.camera.camera2.internal.compat.workaround.ResolutionCorrector;
-import androidx.camera.core.AspectRatio;
-import androidx.camera.core.Logger;
-import androidx.camera.core.ResolutionSelector;
-import androidx.camera.core.impl.ImageOutputConfig;
-import androidx.camera.core.impl.SizeCoordinate;
-import androidx.camera.core.impl.SurfaceConfig;
-import androidx.camera.core.impl.utils.AspectRatioUtil;
-import androidx.camera.core.impl.utils.CameraOrientationUtil;
-import androidx.camera.core.impl.utils.CompareSizesByArea;
-import androidx.camera.core.internal.utils.SizeUtil;
-import androidx.core.util.Preconditions;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-/**
- * The supported output sizes collector to help collect the available resolution candidate list
- * according to the use case config and the following settings in {@link ResolutionSelector}:
- *
- * 1. Preferred aspect ratio
- * 2. Preferred resolution
- * 3. Max resolution
- * 4. Is high resolution enabled
- */
-@RequiresApi(21)
-final class SupportedOutputSizesCollector {
- private static final String TAG = "SupportedOutputSizesCollector";
- @NonNull
- private final CameraCharacteristicsCompat mCharacteristics;
- @NonNull
- private final DisplayInfoManager mDisplayInfoManager;
- private final ResolutionCorrector mResolutionCorrector = new ResolutionCorrector();
- private final Map<Integer, Size[]> mOutputSizesCache = new HashMap<>();
- private final Map<Integer, Size[]> mHighResolutionOutputSizesCache = new HashMap<>();
- private final boolean mIsSensorLandscapeResolution;
- private final boolean mIsBurstCaptureSupported;
- private final Size mActiveArraySize;
- private final int mSensorOrientation;
- private final int mLensFacing;
-
- SupportedOutputSizesCollector(@NonNull String cameraId,
- @NonNull CameraCharacteristicsCompat cameraCharacteristics,
- @NonNull DisplayInfoManager displayInfoManager) {
- mCharacteristics = cameraCharacteristics;
- mDisplayInfoManager = displayInfoManager;
-
- mIsSensorLandscapeResolution = isSensorLandscapeResolution(mCharacteristics);
- mIsBurstCaptureSupported = isBurstCaptureSupported();
-
- Rect rect = mCharacteristics.get(CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE);
- mActiveArraySize = rect != null ? new Size(rect.width(), rect.height()) : null;
-
- mSensorOrientation = mCharacteristics.get(CameraCharacteristics.SENSOR_ORIENTATION);
- mLensFacing = mCharacteristics.get(CameraCharacteristics.LENS_FACING);
- }
-
- /**
- * Collects and sorts the resolution candidate list by the following steps:
- *
- * 1. Collects the candidate list by the high resolution enable setting.
- * 2. Filters out the candidate list according to the min size bound and max resolution.
- * 3. Sorts the candidate list according to the rules of legacy resolution API or new
- * Resolution API.
- * 4. Forces select specific resolutions according to ResolutionCorrector workaround.
- */
- @NonNull
- List<Size> getSupportedOutputSizes(@NonNull ResolutionSelector resolutionSelector,
- int imageFormat, @Nullable Size miniBoundingSize, boolean isHighResolutionDisabled,
- @Nullable Size[] customizedSupportSizes) {
- // 1. Collects the candidate list by the high resolution enable setting.
- List<Size> resolutionCandidateList = collectResolutionCandidateList(resolutionSelector,
- imageFormat, isHighResolutionDisabled, customizedSupportSizes);
-
- // 2. Filters out the candidate list according to the min size bound and max resolution.
- resolutionCandidateList = filterOutResolutionCandidateListBySettings(
- resolutionCandidateList, resolutionSelector);
-
- // 3. Sorts the candidate list according to the rules of new Resolution API.
- resolutionCandidateList = sortResolutionCandidateListByResolutionSelector(
- resolutionCandidateList, resolutionSelector,
- mDisplayInfoManager.getMaxSizeDisplay().getRotation(), miniBoundingSize);
-
- // 4. Forces select specific resolutions according to ResolutionCorrector workaround.
- resolutionCandidateList = mResolutionCorrector.insertOrPrioritize(
- SurfaceConfig.getConfigType(imageFormat), resolutionCandidateList);
-
- return resolutionCandidateList;
- }
-
- /**
- * Collects the resolution candidate list.
- *
- * 1. Customized supported resolutions list will be returned when it exists
- * 2. Otherwise, the sizes retrieved from {@link StreamConfigurationMap#getOutputSizes(int)}
- * will be the base of the resolution candidate list.
- * 3. High resolution sizes retrieved from
- * {@link StreamConfigurationMap#getHighResolutionOutputSizes(int)} will be included when
- * {@link ResolutionSelector#isHighResolutionEnabled()} returns true.
- *
- * The returned list will be sorted in descending order and duplicate items will be removed.
- */
- @NonNull
- private List<Size> collectResolutionCandidateList(
- @NonNull ResolutionSelector resolutionSelector, int imageFormat,
- boolean isHighResolutionDisabled, @Nullable Size[] customizedSupportedSizes) {
- Size[] outputSizes = customizedSupportedSizes;
-
- if (outputSizes == null) {
- boolean highResolutionEnabled =
- !isHighResolutionDisabled && resolutionSelector.isHighResolutionEnabled();
- outputSizes = getAllOutputSizesByFormat(imageFormat, highResolutionEnabled);
- }
-
- // Sort the output sizes. The Comparator result must be reversed to have a descending order
- // result.
- Arrays.sort(outputSizes, new CompareSizesByArea(true));
-
- List<Size> resultList = Arrays.asList(outputSizes);
-
- if (resultList.isEmpty()) {
- throw new IllegalArgumentException(
- "Resolution candidate list is empty when collecting by the settings!");
- }
-
- return resultList;
- }
-
- /**
- * Filters out the resolution candidate list by the max resolution setting.
- *
- * The input size list should have been sorted in descending order.
- */
- private List<Size> filterOutResolutionCandidateListBySettings(
- @NonNull List<Size> resolutionCandidateList,
- @NonNull ResolutionSelector resolutionSelector) {
- // Retrieves the max resolution setting. When ResolutionSelector is used, all resolution
- // selection logic should depend on ResolutionSelector's settings.
- Size maxResolution = resolutionSelector.getMaxResolution();
-
- // Filter out the resolution candidate list by the max resolution. Sizes that any edge
- // exceeds the max resolution will be filtered out.
- List<Size> resultList;
-
- if (maxResolution == null) {
- resultList = new ArrayList<>(resolutionCandidateList);
- } else {
- resultList = new ArrayList<>();
- for (Size outputSize : resolutionCandidateList) {
- if (!SizeUtil.isLongerInAnyEdge(outputSize, maxResolution)) {
- resultList.add(outputSize);
- }
- }
- }
-
- if (resultList.isEmpty()) {
- throw new IllegalArgumentException(
- "Resolution candidate list is empty after filtering out by the settings!");
- }
-
- return resultList;
- }
-
- /**
- * Sorts the resolution candidate list according to the new ResolutionSelector API logic.
- *
- * The list will be sorted by the following order:
- * 1. size of preferred resolution
- * 2. a resolution with preferred aspect ratio, is not smaller than, and is closest to the
- * preferred resolution.
- * 3. resolutions with preferred aspect ratio and is smaller than the preferred resolution
- * size in descending order of resolution area size.
- * 4. Other sizes sorted by CompareAspectRatiosByMappingAreaInFullFovAspectRatioSpace and
- * area size.
- */
- @NonNull
- private List<Size> sortResolutionCandidateListByResolutionSelector(
- @NonNull List<Size> resolutionCandidateList,
- @NonNull ResolutionSelector resolutionSelector,
- @ImageOutputConfig.RotationValue int targetRotation,
- @Nullable Size miniBoundingSize) {
- Rational aspectRatio = getTargetAspectRatioByResolutionSelector(resolutionSelector);
- Preconditions.checkNotNull(aspectRatio, "ResolutionSelector should also have aspect ratio"
- + " value.");
-
- Size targetSize = getTargetSizeByResolutionSelector(resolutionSelector, targetRotation,
- mSensorOrientation, mLensFacing);
- List<Size> resultList = sortResolutionCandidateListByTargetAspectRatioAndSize(
- resolutionCandidateList, aspectRatio, miniBoundingSize);
-
- // Moves the target size to the first position if it exists in the resolution candidate
- // list and there is no quirk that needs to select specific aspect ratio sizes in priority.
- if (resultList.contains(targetSize)) {
- resultList.remove(targetSize);
- resultList.add(0, targetSize);
- }
-
- return resultList;
- }
-
- @NonNull
- private Size[] getAllOutputSizesByFormat(int imageFormat, boolean highResolutionEnabled) {
- Size[] outputs = mOutputSizesCache.get(imageFormat);
- if (outputs == null) {
- outputs = doGetOutputSizesByFormat(imageFormat);
- mOutputSizesCache.put(imageFormat, outputs);
- }
-
- Size[] highResolutionOutputs = null;
-
- // A device that does not support the BURST_CAPTURE capability,
- // StreamConfigurationMap#getHighResolutionOutputSizes() will return null.
- if (highResolutionEnabled && mIsBurstCaptureSupported) {
- highResolutionOutputs = mHighResolutionOutputSizesCache.get(imageFormat);
-
- // High resolution output sizes list may be empty. If it is empty and cached in the
- // map, don't need to query it again.
- if (highResolutionOutputs == null && !mHighResolutionOutputSizesCache.containsKey(
- imageFormat)) {
- highResolutionOutputs = doGetHighResolutionOutputSizesByFormat(imageFormat);
- mHighResolutionOutputSizesCache.put(imageFormat, highResolutionOutputs);
- }
- }
-
- // Combines output sizes if high resolution sizes list is not empty.
- if (highResolutionOutputs != null) {
- Size[] allOutputs = Arrays.copyOf(highResolutionOutputs,
- highResolutionOutputs.length + outputs.length);
- System.arraycopy(outputs, 0, allOutputs, highResolutionOutputs.length, outputs.length);
- outputs = allOutputs;
- }
-
- return outputs;
- }
-
- @NonNull
- private Size[] doGetOutputSizesByFormat(int imageFormat) {
- StreamConfigurationMapCompat mapCompat = mCharacteristics.getStreamConfigurationMapCompat();
- Size[] outputSizes = mapCompat.getOutputSizes(imageFormat);
- if (outputSizes == null) {
- throw new IllegalArgumentException(
- "Can not get supported output size for the format: " + imageFormat);
- }
-
- return outputSizes;
- }
-
- @Nullable
- private Size[] doGetHighResolutionOutputSizesByFormat(int imageFormat) {
- if (Build.VERSION.SDK_INT < 23) {
- return null;
- }
-
- Size[] outputSizes;
-
- StreamConfigurationMap map =
- mCharacteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
-
- if (map == null) {
- throw new IllegalArgumentException("Can not retrieve SCALER_STREAM_CONFIGURATION_MAP");
- }
-
- outputSizes = Api23Impl.getHighResolutionOutputSizes(map, imageFormat);
-
- return outputSizes;
- }
-
- /**
- * Returns the target aspect ratio rational value.
- *
- * @param resolutionSelector the resolution selector of the use case.
- */
- @Nullable
- private Rational getTargetAspectRatioByResolutionSelector(
- @NonNull ResolutionSelector resolutionSelector) {
- Rational outputRatio = null;
-
- @AspectRatio.Ratio int aspectRatio = resolutionSelector.getPreferredAspectRatio();
- switch (aspectRatio) {
- case AspectRatio.RATIO_4_3:
- outputRatio = mIsSensorLandscapeResolution ? ASPECT_RATIO_4_3
- : ASPECT_RATIO_3_4;
- break;
- case AspectRatio.RATIO_16_9:
- outputRatio = mIsSensorLandscapeResolution ? ASPECT_RATIO_16_9
- : ASPECT_RATIO_9_16;
- break;
- case AspectRatio.RATIO_DEFAULT:
- break;
- default:
- Logger.e(TAG, "Undefined target aspect ratio: " + aspectRatio);
- }
- return outputRatio;
- }
-
- @Nullable
- static Size getTargetSizeByResolutionSelector(@NonNull ResolutionSelector resolutionSelector,
- int targetRotation, int sensorOrientation, int lensFacing) {
- Size targetSize = resolutionSelector.getPreferredResolution();
-
- // Calibrate targetSize by the target rotation value if it is set by the Android View
- // coordinate orientation.
- if (resolutionSelector.getSizeCoordinate() == SizeCoordinate.ANDROID_VIEW) {
- targetSize = flipSizeByRotation(targetSize, targetRotation, lensFacing,
- sensorOrientation);
- }
- return targetSize;
- }
-
- private static boolean isRotationNeeded(int targetRotation, int lensFacing,
- int sensorOrientation) {
- int relativeRotationDegrees =
- CameraOrientationUtil.surfaceRotationToDegrees(targetRotation);
-
- // Currently this assumes that a back-facing camera is always opposite to the screen.
- // This may not be the case for all devices, so in the future we may need to handle that
- // scenario.
- boolean isOppositeFacingScreen = CameraCharacteristics.LENS_FACING_BACK == lensFacing;
-
- int sensorRotationDegrees = CameraOrientationUtil.getRelativeImageRotation(
- relativeRotationDegrees,
- sensorOrientation,
- isOppositeFacingScreen);
- return sensorRotationDegrees == 90 || sensorRotationDegrees == 270;
- }
-
- /**
- * Sorts the resolution candidate list according to the target aspect ratio and size settings.
- *
- * 1. The resolution candidate list will be grouped by aspect ratio.
- * 2. Each group only keeps one size which is not smaller than the target size.
- * 3. The aspect ratios of groups will be sorted against to the target aspect ratio setting by
- * CompareAspectRatiosByMappingAreaInFullFovAspectRatioSpace.
- * 4. Concatenate all sizes as the result list
- */
- @NonNull
- private List<Size> sortResolutionCandidateListByTargetAspectRatioAndSize(
- @NonNull List<Size> resolutionCandidateList, @NonNull Rational aspectRatio,
- @Nullable Size miniBoundingSize) {
- // Rearrange the supported size to put the ones with the same aspect ratio in the front
- // of the list and put others in the end from large to small. Some low end devices may
- // not able to get an supported resolution that match the preferred aspect ratio.
-
- // Group output sizes by aspect ratio.
- Map<Rational, List<Size>> aspectRatioSizeListMap =
- groupSizesByAspectRatio(resolutionCandidateList);
-
- // If the target resolution is set, use it to remove unnecessary larger sizes.
- if (miniBoundingSize != null) {
- // Remove unnecessary larger sizes from each aspect ratio size list
- for (Rational key : aspectRatioSizeListMap.keySet()) {
- removeSupportedSizesByMiniBoundingSize(aspectRatioSizeListMap.get(key),
- miniBoundingSize);
- }
- }
-
- // Sort the aspect ratio key set by the target aspect ratio.
- List<Rational> aspectRatios = new ArrayList<>(aspectRatioSizeListMap.keySet());
- Rational fullFovRatio = mActiveArraySize != null ? new Rational(
- mActiveArraySize.getWidth(), mActiveArraySize.getHeight()) : null;
- Collections.sort(aspectRatios,
- new AspectRatioUtil.CompareAspectRatiosByMappingAreaInFullFovAspectRatioSpace(
- aspectRatio, fullFovRatio));
-
- List<Size> resultList = new ArrayList<>();
-
- // Put available sizes into final result list by aspect ratio distance to target ratio.
- for (Rational rational : aspectRatios) {
- for (Size size : aspectRatioSizeListMap.get(rational)) {
- // A size may exist in multiple groups in mod16 condition. Keep only one in
- // the final list.
- if (!resultList.contains(size)) {
- resultList.add(size);
- }
- }
- }
-
- return resultList;
- }
-
- private boolean isBurstCaptureSupported() {
- int[] availableCapabilities =
- mCharacteristics.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
-
- if (availableCapabilities != null) {
- for (int capability : availableCapabilities) {
- if (capability
- == CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE) {
- return true;
- }
- }
- }
-
- return false;
- }
-
- //////////////////////////////////////////////////////////////////////////////////////////
- // The following functions can be reused by the legacy resolution selection logic. The can be
- // changed as private function after the legacy resolution API is completely removed.
- //////////////////////////////////////////////////////////////////////////////////////////
-
- // Use target rotation to calibrate the size.
- @Nullable
- static Size flipSizeByRotation(@Nullable Size size, int targetRotation, int lensFacing,
- int sensorOrientation) {
- Size outputSize = size;
- // Calibrates the size with the display and sensor rotation degrees values.
- if (size != null && isRotationNeeded(targetRotation, lensFacing, sensorOrientation)) {
- outputSize = new Size(/* width= */size.getHeight(), /* height= */size.getWidth());
- }
- return outputSize;
- }
-
- static Map<Rational, List<Size>> groupSizesByAspectRatio(List<Size> sizes) {
- Map<Rational, List<Size>> aspectRatioSizeListMap = new HashMap<>();
-
- List<Rational> aspectRatioKeys = getResolutionListGroupingAspectRatioKeys(sizes);
-
- for (Rational aspectRatio: aspectRatioKeys) {
- aspectRatioSizeListMap.put(aspectRatio, new ArrayList<>());
- }
-
- for (Size outputSize : sizes) {
- for (Rational key : aspectRatioSizeListMap.keySet()) {
- // Put the size into all groups that is matched in mod16 condition since a size
- // may match multiple aspect ratio in mod16 algorithm.
- if (hasMatchingAspectRatio(outputSize, key)) {
- aspectRatioSizeListMap.get(key).add(outputSize);
- }
- }
- }
-
- return aspectRatioSizeListMap;
- }
-
- /**
- * Returns the grouping aspect ratio keys of the input resolution list.
- *
- * <p>Some sizes might be mod16 case. When grouping, those sizes will be grouped into an
- * existing aspect ratio group if the aspect ratio can match by the mod16 rule.
- */
- @NonNull
- static List<Rational> getResolutionListGroupingAspectRatioKeys(
- @NonNull List<Size> resolutionCandidateList) {
- List<Rational> aspectRatios = new ArrayList<>();
-
- // Adds the default 4:3 and 16:9 items first to avoid their mod16 sizes to create
- // additional items.
- aspectRatios.add(ASPECT_RATIO_4_3);
- aspectRatios.add(ASPECT_RATIO_16_9);
-
- // Tries to find the aspect ratio which the target size belongs to.
- for (Size size : resolutionCandidateList) {
- Rational newRatio = new Rational(size.getWidth(), size.getHeight());
- boolean aspectRatioFound = aspectRatios.contains(newRatio);
-
- // The checking size might be a mod16 size which can be mapped to an existing aspect
- // ratio group.
- if (!aspectRatioFound) {
- boolean hasMatchingAspectRatio = false;
- for (Rational aspectRatio : aspectRatios) {
- if (hasMatchingAspectRatio(size, aspectRatio)) {
- hasMatchingAspectRatio = true;
- break;
- }
- }
- if (!hasMatchingAspectRatio) {
- aspectRatios.add(newRatio);
- }
- }
- }
-
- return aspectRatios;
- }
-
- /**
- * Removes unnecessary sizes by target size.
- *
- * <p>If the target resolution is set, a size that is equal to or closest to the target
- * resolution will be selected. If the list includes more than one size equal to or larger
- * than the target resolution, only one closest size needs to be kept. The other larger sizes
- * can be removed so that they won't be selected to use.
- *
- * @param supportedSizesList The list should have been sorted in descending order.
- * @param miniBoundingSize The target size used to remove unnecessary sizes.
- */
- static void removeSupportedSizesByMiniBoundingSize(List<Size> supportedSizesList,
- Size miniBoundingSize) {
- if (supportedSizesList == null || supportedSizesList.isEmpty()) {
- return;
- }
-
- int indexBigEnough = -1;
- List<Size> removeSizes = new ArrayList<>();
-
- // Get the index of the item that is equal to or closest to the target size.
- for (int i = 0; i < supportedSizesList.size(); i++) {
- Size outputSize = supportedSizesList.get(i);
- if (outputSize.getWidth() >= miniBoundingSize.getWidth()
- && outputSize.getHeight() >= miniBoundingSize.getHeight()) {
- // New big enough item closer to the target size is found. Adding the previous
- // one into the sizes list that will be removed.
- if (indexBigEnough >= 0) {
- removeSizes.add(supportedSizesList.get(indexBigEnough));
- }
-
- indexBigEnough = i;
- } else {
- // If duplicated miniBoundingSize items exist in the list, the size will be added
- // into the removeSizes list. Removes it from the removeSizes list to keep the
- // miniBoundingSize items in the final result list.
- if (indexBigEnough >= 0) {
- removeSizes.remove(supportedSizesList.get(indexBigEnough));
- }
- break;
- }
- }
-
- // Remove the unnecessary items that are larger than the item closest to the target size.
- supportedSizesList.removeAll(removeSizes);
- }
-
- static boolean isSensorLandscapeResolution(
- @NonNull CameraCharacteristicsCompat characteristicsCompat) {
- Size pixelArraySize =
- characteristicsCompat.get(CameraCharacteristics.SENSOR_INFO_PIXEL_ARRAY_SIZE);
-
- // Make the default value is true since usually the sensor resolution is landscape.
- return pixelArraySize == null || pixelArraySize.getWidth() >= pixelArraySize.getHeight();
- }
-
- //////////////////////////////////////////////////////////////////////////////////////////
- // The above functions can be reused by the legacy resolution selection logic. The can be
- // changed as private function after the legacy resolution API is completely removed.
- //////////////////////////////////////////////////////////////////////////////////////////
-
- @RequiresApi(23)
- private static class Api23Impl {
- private Api23Impl() {
- // This class is not instantiable.
- }
-
- @DoNotInline
- static Size[] getHighResolutionOutputSizes(StreamConfigurationMap streamConfigurationMap,
- int format) {
- return streamConfigurationMap.getHighResolutionOutputSizes(format);
- }
- }
-}
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/SupportedSurfaceCombination.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/SupportedSurfaceCombination.java
index 5b19fe1..628d8be 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/SupportedSurfaceCombination.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/SupportedSurfaceCombination.java
@@ -18,38 +18,19 @@
import static android.content.pm.PackageManager.FEATURE_CAMERA_CONCURRENT;
-import static androidx.camera.camera2.internal.SupportedOutputSizesCollector.flipSizeByRotation;
-import static androidx.camera.camera2.internal.SupportedOutputSizesCollector.getResolutionListGroupingAspectRatioKeys;
-import static androidx.camera.camera2.internal.SupportedOutputSizesCollector.getTargetSizeByResolutionSelector;
-import static androidx.camera.camera2.internal.SupportedOutputSizesCollector.groupSizesByAspectRatio;
-import static androidx.camera.camera2.internal.SupportedOutputSizesCollector.isSensorLandscapeResolution;
-import static androidx.camera.camera2.internal.SupportedOutputSizesCollector.removeSupportedSizesByMiniBoundingSize;
-import static androidx.camera.core.impl.utils.AspectRatioUtil.ASPECT_RATIO_16_9;
-import static androidx.camera.core.impl.utils.AspectRatioUtil.ASPECT_RATIO_3_4;
-import static androidx.camera.core.impl.utils.AspectRatioUtil.ASPECT_RATIO_4_3;
-import static androidx.camera.core.impl.utils.AspectRatioUtil.ASPECT_RATIO_9_16;
-import static androidx.camera.core.impl.utils.AspectRatioUtil.hasMatchingAspectRatio;
import static androidx.camera.core.internal.utils.SizeUtil.RESOLUTION_1080P;
import static androidx.camera.core.internal.utils.SizeUtil.RESOLUTION_480P;
-import static androidx.camera.core.internal.utils.SizeUtil.RESOLUTION_VGA;
-import static androidx.camera.core.internal.utils.SizeUtil.RESOLUTION_ZERO;
-import static androidx.camera.core.internal.utils.SizeUtil.getArea;
import android.content.Context;
-import android.graphics.Rect;
import android.graphics.SurfaceTexture;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.params.StreamConfigurationMap;
import android.media.CamcorderProfile;
import android.media.MediaRecorder;
-import android.util.Pair;
import android.util.Range;
-import android.util.Rational;
import android.util.Size;
-import android.view.Surface;
import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.annotation.VisibleForTesting;
import androidx.camera.camera2.internal.compat.CameraAccessExceptionCompat;
@@ -57,20 +38,14 @@
import androidx.camera.camera2.internal.compat.CameraManagerCompat;
import androidx.camera.camera2.internal.compat.StreamConfigurationMapCompat;
import androidx.camera.camera2.internal.compat.workaround.ExtraSupportedSurfaceCombinationsContainer;
-import androidx.camera.camera2.internal.compat.workaround.ResolutionCorrector;
-import androidx.camera.core.AspectRatio;
import androidx.camera.core.CameraUnavailableException;
-import androidx.camera.core.Logger;
-import androidx.camera.core.ResolutionSelector;
import androidx.camera.core.impl.AttachedSurfaceInfo;
import androidx.camera.core.impl.ImageFormatConstants;
-import androidx.camera.core.impl.ImageOutputConfig;
import androidx.camera.core.impl.StreamSpec;
import androidx.camera.core.impl.SurfaceCombination;
import androidx.camera.core.impl.SurfaceConfig;
import androidx.camera.core.impl.SurfaceSizeDefinition;
import androidx.camera.core.impl.UseCaseConfig;
-import androidx.camera.core.impl.utils.AspectRatioUtil;
import androidx.camera.core.impl.utils.CompareSizesByArea;
import androidx.core.util.Preconditions;
@@ -101,19 +76,12 @@
private final ExtraSupportedSurfaceCombinationsContainer
mExtraSupportedSurfaceCombinationsContainer;
private final int mHardwareLevel;
- private final boolean mIsSensorLandscapeResolution;
private boolean mIsRawSupported = false;
private boolean mIsBurstCaptureSupported = false;
@VisibleForTesting
SurfaceSizeDefinition mSurfaceSizeDefinition;
- private final Map<Integer, Size[]> mOutputSizesCache = new HashMap<>();
@NonNull
private final DisplayInfoManager mDisplayInfoManager;
- private final ResolutionCorrector mResolutionCorrector = new ResolutionCorrector();
- private final Size mActiveArraySize;
- private final int mSensorOrientation;
- private final int mLensFacing;
- private final SupportedOutputSizesCollector mSupportedOutputSizesCollector;
SupportedSurfaceCombination(@NonNull Context context, @NonNull String cameraId,
@NonNull CameraManagerCompat cameraManagerCompat,
@@ -131,7 +99,6 @@
CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
mHardwareLevel = keyValue != null ? keyValue
: CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY;
- mIsSensorLandscapeResolution = isSensorLandscapeResolution(mCharacteristics);
} catch (CameraAccessExceptionCompat e) {
throw CameraUnavailableExceptionHelper.createFrom(e);
}
@@ -150,21 +117,12 @@
}
}
- Rect rect = mCharacteristics.get(CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE);
- mActiveArraySize = rect != null ? new Size(rect.width(), rect.height()) : null;
-
- mSensorOrientation = mCharacteristics.get(CameraCharacteristics.SENSOR_ORIENTATION);
- mLensFacing = mCharacteristics.get(CameraCharacteristics.LENS_FACING);
-
generateSupportedCombinationList();
if (context.getPackageManager().hasSystemFeature(FEATURE_CAMERA_CONCURRENT)) {
generateConcurrentSupportedCombinationList();
}
generateSurfaceSizeDefinition();
checkCustomization();
-
- mSupportedOutputSizesCollector = new SupportedOutputSizesCollector(mCameraId,
- mCharacteristics, mDisplayInfoManager);
}
String getCameraId() {
@@ -284,9 +242,11 @@
/**
* Finds the suggested stream specifications of the newly added UseCaseConfig.
*
- * @param isConcurrentCameraModeOn true if concurrent camera mode is on, otherwise false.
- * @param attachedSurfaces the existing surfaces.
- * @param newUseCaseConfigs newly added UseCaseConfig.
+ * @param isConcurrentCameraModeOn true if concurrent camera mode is on, otherwise
+ * false.
+ * @param attachedSurfaces the existing surfaces.
+ * @param newUseCaseConfigsSupportedSizeMap newly added UseCaseConfig to supported output
+ * sizes map.
* @return the suggested stream specifications, which is a mapping from UseCaseConfig to the
* suggested stream specification.
* @throws IllegalArgumentException if the suggested solution for newUseCaseConfigs cannot be
@@ -297,7 +257,7 @@
Map<UseCaseConfig<?>, StreamSpec> getSuggestedStreamSpecifications(
boolean isConcurrentCameraModeOn,
@NonNull List<AttachedSurfaceInfo> attachedSurfaces,
- @NonNull List<UseCaseConfig<?>> newUseCaseConfigs) {
+ @NonNull Map<UseCaseConfig<?>, List<Size>> newUseCaseConfigsSupportedSizeMap) {
// Refresh Preview Size based on current display configurations.
refreshPreviewSize();
List<SurfaceConfig> surfaceConfigs = new ArrayList<>();
@@ -305,6 +265,8 @@
surfaceConfigs.add(attachedSurface.getSurfaceConfig());
}
+ List<UseCaseConfig<?>> newUseCaseConfigs = new ArrayList<>(
+ newUseCaseConfigsSupportedSizeMap.keySet());
// Use the small size (640x480) for new use cases to check whether there is any possible
// supported combination first
for (UseCaseConfig<?> useCaseConfig : newUseCaseConfigs) {
@@ -347,7 +309,8 @@
// Collect supported output sizes for all use cases
for (Integer index : useCasesPriorityOrder) {
- List<Size> supportedOutputSizes = getSupportedOutputSizes(newUseCaseConfigs.get(index));
+ List<Size> supportedOutputSizes = newUseCaseConfigsSupportedSizeMap.get(
+ newUseCaseConfigs.get(index));
supportedOutputSizesList.add(supportedOutputSizes);
}
@@ -455,48 +418,6 @@
return suggestedStreamSpecMap;
}
- /**
- * Returns the target aspect ratio rational value.
- *
- * @param imageOutputConfig the image output config of the use case.
- * @param resolutionCandidateList the resolution candidate list which will be used to
- * determine the aspect ratio by target size when target
- * aspect ratio setting is not set.
- */
- private Rational getTargetAspectRatio(@NonNull ImageOutputConfig imageOutputConfig,
- @NonNull List<Size> resolutionCandidateList) {
- Rational outputRatio = null;
-
- if (imageOutputConfig.hasTargetAspectRatio()) {
- @AspectRatio.Ratio int aspectRatio = imageOutputConfig.getTargetAspectRatio();
- switch (aspectRatio) {
- case AspectRatio.RATIO_4_3:
- outputRatio = mIsSensorLandscapeResolution ? ASPECT_RATIO_4_3
- : ASPECT_RATIO_3_4;
- break;
- case AspectRatio.RATIO_16_9:
- outputRatio = mIsSensorLandscapeResolution ? ASPECT_RATIO_16_9
- : ASPECT_RATIO_9_16;
- break;
- case AspectRatio.RATIO_DEFAULT:
- break;
- default:
- Logger.e(TAG, "Undefined target aspect ratio: " + aspectRatio);
- }
- } else {
- // The legacy resolution API will use the aspect ratio of the target size to
- // be the fallback target aspect ratio value when the use case has no target
- // aspect ratio setting.
- Size targetSize = getTargetSize(imageOutputConfig);
- if (targetSize != null) {
- outputRatio = getAspectRatioGroupKeyOfTargetSize(targetSize,
- resolutionCandidateList);
- }
- }
-
- return outputRatio;
- }
-
private List<Integer> getUseCasesPriorityOrder(List<UseCaseConfig<?>> newUseCaseConfigs) {
List<Integer> priorityOrder = new ArrayList<>();
@@ -531,180 +452,6 @@
return priorityOrder;
}
- @NonNull
- @VisibleForTesting
- List<Size> getSupportedOutputSizes(@NonNull UseCaseConfig<?> config) {
- int imageFormat = config.getInputFormat();
- ImageOutputConfig imageOutputConfig = (ImageOutputConfig) config;
-
- List<Size> customOrderedResolutions = imageOutputConfig.getCustomOrderedResolutions(null);
- if (customOrderedResolutions != null) {
- return customOrderedResolutions;
- }
- ResolutionSelector resolutionSelector = imageOutputConfig.getResolutionSelector(null);
- if (resolutionSelector != null) {
- Size miniBoundingSize = imageOutputConfig.getDefaultResolution(null);
-
- if (resolutionSelector.getPreferredResolution() != null) {
- miniBoundingSize = getTargetSizeByResolutionSelector(resolutionSelector,
- mDisplayInfoManager.getMaxSizeDisplay().getRotation(), mSensorOrientation,
- mLensFacing);
- }
-
- return mSupportedOutputSizesCollector.getSupportedOutputSizes(resolutionSelector,
- imageFormat, miniBoundingSize, config.isHigResolutionDisabled(false),
- getCustomizedSupportSizesFromConfig(imageFormat, imageOutputConfig));
- }
-
- Size[] outputSizes = getCustomizedSupportSizesFromConfig(imageFormat, imageOutputConfig);
- if (outputSizes == null) {
- outputSizes = getAllOutputSizesByFormat(imageFormat);
- }
-
- // Sort the result sizes. The Comparator result must be reversed to have a descending
- // order result.
- Arrays.sort(outputSizes, new CompareSizesByArea(true));
-
- List<Size> outputSizeCandidates = new ArrayList<>();
- Size maxSize = imageOutputConfig.getMaxResolution(null);
- Size maxOutputSizeByFormat = getMaxOutputSizeByFormat(imageFormat);
-
- // Set maxSize as the max resolution setting or the max supported output size for the
- // image format, whichever is smaller.
- if (maxSize == null || getArea(maxOutputSizeByFormat) < getArea(maxSize)) {
- maxSize = maxOutputSizeByFormat;
- }
-
- // Sort the output sizes. The Comparator result must be reversed to have a descending order
- // result.
- Arrays.sort(outputSizes, new CompareSizesByArea(true));
-
- Size targetSize = getTargetSize(imageOutputConfig);
- Size minSize = RESOLUTION_VGA;
- int defaultSizeArea = getArea(RESOLUTION_VGA);
- int maxSizeArea = getArea(maxSize);
- // When maxSize is smaller than 640x480, set minSize as 0x0. It means the min size bound
- // will be ignored. Otherwise, set the minimal size according to min(DEFAULT_SIZE,
- // TARGET_RESOLUTION).
- if (maxSizeArea < defaultSizeArea) {
- minSize = RESOLUTION_ZERO;
- } else if (targetSize != null && getArea(targetSize) < defaultSizeArea) {
- minSize = targetSize;
- }
-
- // Filter out the ones that exceed the maximum size and the minimum size. The output
- // sizes candidates list won't have duplicated items.
- for (Size outputSize : outputSizes) {
- if (getArea(outputSize) <= getArea(maxSize) && getArea(outputSize) >= getArea(minSize)
- && !outputSizeCandidates.contains(outputSize)) {
- outputSizeCandidates.add(outputSize);
- }
- }
-
- if (outputSizeCandidates.isEmpty()) {
- throw new IllegalArgumentException(
- "Can not get supported output size under supported maximum for the format: "
- + imageFormat);
- }
-
- Rational aspectRatio = getTargetAspectRatio(imageOutputConfig, outputSizeCandidates);
-
- // Check the default resolution if the target resolution is not set
- targetSize = targetSize == null ? imageOutputConfig.getDefaultResolution(null) : targetSize;
-
- List<Size> supportedResolutions = new ArrayList<>();
- Map<Rational, List<Size>> aspectRatioSizeListMap = new HashMap<>();
-
- if (aspectRatio == null) {
- // If no target aspect ratio is set, all sizes can be added to the result list
- // directly. No need to sort again since the source list has been sorted previously.
- supportedResolutions.addAll(outputSizeCandidates);
-
- // If the target resolution is set, use it to remove unnecessary larger sizes.
- if (targetSize != null) {
- removeSupportedSizesByMiniBoundingSize(supportedResolutions, targetSize);
- }
- } else {
- // Rearrange the supported size to put the ones with the same aspect ratio in the front
- // of the list and put others in the end from large to small. Some low end devices may
- // not able to get an supported resolution that match the preferred aspect ratio.
-
- // Group output sizes by aspect ratio.
- aspectRatioSizeListMap = groupSizesByAspectRatio(outputSizeCandidates);
-
- // If the target resolution is set, use it to remove unnecessary larger sizes.
- if (targetSize != null) {
- // Remove unnecessary larger sizes from each aspect ratio size list
- for (Rational key : aspectRatioSizeListMap.keySet()) {
- removeSupportedSizesByMiniBoundingSize(aspectRatioSizeListMap.get(key),
- targetSize);
- }
- }
-
- // Sort the aspect ratio key set by the target aspect ratio.
- List<Rational> aspectRatios = new ArrayList<>(aspectRatioSizeListMap.keySet());
- Rational fullFovRatio = mActiveArraySize != null ? new Rational(
- mActiveArraySize.getWidth(), mActiveArraySize.getHeight()) : null;
- Collections.sort(aspectRatios,
- new AspectRatioUtil.CompareAspectRatiosByMappingAreaInFullFovAspectRatioSpace(
- aspectRatio, fullFovRatio));
-
- // Put available sizes into final result list by aspect ratio distance to target ratio.
- for (Rational rational : aspectRatios) {
- for (Size size : aspectRatioSizeListMap.get(rational)) {
- // A size may exist in multiple groups in mod16 condition. Keep only one in
- // the final list.
- if (!supportedResolutions.contains(size)) {
- supportedResolutions.add(size);
- }
- }
- }
- }
-
- supportedResolutions = mResolutionCorrector.insertOrPrioritize(
- SurfaceConfig.getConfigType(config.getInputFormat()),
- supportedResolutions);
-
- return supportedResolutions;
- }
-
- @Nullable
- private Size getTargetSize(@NonNull ImageOutputConfig imageOutputConfig) {
- int targetRotation = imageOutputConfig.getTargetRotation(Surface.ROTATION_0);
- // Calibrate targetSize by the target rotation value.
- Size targetSize = imageOutputConfig.getTargetResolution(null);
- targetSize = flipSizeByRotation(targetSize, targetRotation, mLensFacing,
- mSensorOrientation);
- return targetSize;
- }
-
- /**
- * Returns the aspect ratio group key of the target size when grouping the input resolution
- * candidate list.
- *
- * The resolution candidate list will be grouped with mod 16 consideration. Therefore, we
- * also need to consider the mod 16 factor to find which aspect ratio of group the target size
- * might be put in. So that sizes of the group will be selected to use in the highest priority.
- */
- @Nullable
- private Rational getAspectRatioGroupKeyOfTargetSize(@Nullable Size targetSize,
- @NonNull List<Size> resolutionCandidateList) {
- if (targetSize == null) {
- return null;
- }
-
- List<Rational> aspectRatios = getResolutionListGroupingAspectRatioKeys(
- resolutionCandidateList);
-
- for (Rational aspectRatio: aspectRatios) {
- if (hasMatchingAspectRatio(targetSize, aspectRatio)) {
- return aspectRatio;
- }
- }
-
- return new Rational(targetSize.getWidth(), targetSize.getHeight());
- }
-
private List<List<Size>> getAllPossibleSizeArrangements(
List<List<Size>> supportedOutputSizesList) {
int totalArrangementsCount = 1;
@@ -758,50 +505,6 @@
return allPossibleSizeArrangements;
}
- @Nullable
- private Size[] getCustomizedSupportSizesFromConfig(int imageFormat,
- @NonNull ImageOutputConfig config) {
- Size[] outputSizes = null;
-
- // Try to retrieve customized supported resolutions from config.
- List<Pair<Integer, Size[]>> formatResolutionsPairList =
- config.getSupportedResolutions(null);
-
- if (formatResolutionsPairList != null) {
- for (Pair<Integer, Size[]> formatResolutionPair : formatResolutionsPairList) {
- if (formatResolutionPair.first == imageFormat) {
- outputSizes = formatResolutionPair.second;
- break;
- }
- }
- }
-
- return outputSizes;
- }
-
- @NonNull
- private Size[] getAllOutputSizesByFormat(int imageFormat) {
- Size[] outputs = mOutputSizesCache.get(imageFormat);
- if (outputs == null) {
- outputs = doGetAllOutputSizesByFormat(imageFormat);
- mOutputSizesCache.put(imageFormat, outputs);
- }
-
- return outputs;
- }
-
- @NonNull
- private Size[] doGetAllOutputSizesByFormat(int imageFormat) {
- StreamConfigurationMapCompat mapCompat = mCharacteristics.getStreamConfigurationMapCompat();
- Size[] outputSizes = mapCompat.getOutputSizes(imageFormat);
- if (outputSizes == null) {
- throw new IllegalArgumentException(
- "Can not get supported output size for the format: " + imageFormat);
- }
-
- return outputSizes;
- }
-
/**
* Get max supported output size for specific image format
*
diff --git a/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/SupportedOutputSizesCollectorTest.kt b/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/SupportedOutputSizesCollectorTest.kt
deleted file mode 100644
index 7761531..0000000
--- a/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/SupportedOutputSizesCollectorTest.kt
+++ /dev/null
@@ -1,1386 +0,0 @@
-/*
- * 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.SurfaceTexture
-import android.hardware.camera2.CameraCharacteristics
-import android.hardware.camera2.CameraManager
-import android.hardware.camera2.params.StreamConfigurationMap
-import android.media.MediaRecorder
-import android.os.Build
-import android.util.Pair
-import android.util.Size
-import android.view.Surface
-import android.view.WindowManager
-import androidx.camera.camera2.Camera2Config
-import androidx.camera.camera2.internal.compat.CameraCharacteristicsCompat
-import androidx.camera.camera2.internal.compat.CameraManagerCompat
-import androidx.camera.core.AspectRatio
-import androidx.camera.core.CameraSelector
-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.ResolutionSelector
-import androidx.camera.core.UseCase
-import androidx.camera.core.impl.CameraDeviceSurfaceManager
-import androidx.camera.core.impl.ImageOutputConfig
-import androidx.camera.core.impl.SizeCoordinate
-import androidx.camera.core.impl.UseCaseConfigFactory
-import androidx.camera.testing.CameraUtil
-import androidx.camera.testing.CameraXUtil
-import androidx.camera.testing.Configs
-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.test.core.app.ApplicationProvider
-import com.google.common.truth.Truth.assertThat
-import java.util.concurrent.ExecutionException
-import java.util.concurrent.TimeUnit
-import org.junit.After
-import org.junit.Assert
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.ArgumentMatchers
-import org.mockito.ArgumentMatchers.anyInt
-import org.mockito.ArgumentMatchers.eq
-import org.mockito.Mockito
-import org.robolectric.ParameterizedRobolectricTestRunner
-import org.robolectric.Shadows
-import org.robolectric.annotation.Config
-import org.robolectric.annotation.internal.DoNotInstrument
-import org.robolectric.shadow.api.Shadow
-import org.robolectric.shadows.ShadowCameraCharacteristics
-import org.robolectric.shadows.ShadowCameraManager
-
-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_ASPECT_RATIO = -1
-private const val DEFAULT_CAMERA_ID = "0"
-private const val SENSOR_ORIENTATION_0 = 0
-private const val SENSOR_ORIENTATION_90 = 90
-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 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 [SupportedOutputSizesCollector] class */
-@RunWith(ParameterizedRobolectricTestRunner::class)
-@DoNotInstrument
-@Config(minSdk = Build.VERSION_CODES.LOLLIPOP)
-class SupportedOutputSizesCollectorTest(
- private val sizeCoordinate: SizeCoordinate
-) {
- private val mockCamcorderProfileHelper = Mockito.mock(CamcorderProfileHelper::class.java)
- private lateinit var cameraManagerCompat: CameraManagerCompat
- private lateinit var cameraCharacteristicsCompat: CameraCharacteristicsCompat
- private lateinit var displayInfoManager: DisplayInfoManager
- private val context = ApplicationProvider.getApplicationContext<Context>()
- private var cameraFactory: FakeCameraFactory? = null
- private var useCaseConfigFactory: UseCaseConfigFactory? = null
-
- @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)
-
- displayInfoManager = DisplayInfoManager.getInstance(context)
- }
-
- @After
- fun tearDown() {
- CameraXUtil.shutdown()[10000, TimeUnit.MILLISECONDS]
- }
-
- @Test
- fun getSupportedOutputSizes_aspectRatio4x3() {
- setupCameraAndInitCameraX(
- hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
- )
- val supportedOutputSizesCollector = SupportedOutputSizesCollector(
- DEFAULT_CAMERA_ID,
- cameraCharacteristicsCompat,
- displayInfoManager
- )
- val useCase = createUseCaseByResolutionSelector(
- FAKE_USE_CASE,
- preferredAspectRatio = AspectRatio.RATIO_4_3
- )
-
- val resultList = getSupportedOutputSizes(supportedOutputSizesCollector, useCase)
- val expectedList = listOf(
- // Matched preferred AspectRatio items, sorted by area size.
- Size(4032, 3024),
- Size(1920, 1440),
- Size(1280, 960),
- Size(640, 480),
- Size(320, 240),
- // Mismatched preferred AspectRatio items, sorted by area size.
- Size(3840, 2160),
- Size(1920, 1080),
- Size(1280, 720),
- Size(960, 544),
- Size(800, 450),
- Size(320, 180),
- Size(256, 144)
- )
- assertThat(resultList).isEqualTo(expectedList)
- }
-
- @Test
- fun getSupportedOutputSizes_aspectRatio16x9_InLimitedDevice() {
- setupCameraAndInitCameraX(
- hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
- )
- val supportedOutputSizesCollector = SupportedOutputSizesCollector(
- DEFAULT_CAMERA_ID,
- cameraCharacteristicsCompat,
- displayInfoManager
- )
- val useCase = createUseCaseByResolutionSelector(
- FAKE_USE_CASE,
- preferredAspectRatio = AspectRatio.RATIO_16_9
- )
-
- val resultList = getSupportedOutputSizes(supportedOutputSizesCollector, useCase)
- val expectedList = listOf(
- // Matched preferred AspectRatio items, sorted by area size.
- Size(3840, 2160),
- Size(1920, 1080),
- Size(1280, 720),
- Size(960, 544),
- Size(800, 450),
- Size(320, 180),
- Size(256, 144),
- // Mismatched preferred AspectRatio items, sorted by area size.
- Size(4032, 3024),
- Size(1920, 1440),
- Size(1280, 960),
- Size(640, 480),
- Size(320, 240)
- )
- assertThat(resultList).isEqualTo(expectedList)
- }
-
- @Test
- fun getSupportedOutputSizes_aspectRatio16x9_inLegacyDevice() {
- setupCameraAndInitCameraX()
- val supportedOutputSizesCollector = SupportedOutputSizesCollector(
- DEFAULT_CAMERA_ID,
- cameraCharacteristicsCompat,
- displayInfoManager
- )
- val useCase = createUseCaseByResolutionSelector(
- FAKE_USE_CASE,
- preferredAspectRatio = AspectRatio.RATIO_16_9
- )
- val resultList = getSupportedOutputSizes(supportedOutputSizesCollector, useCase)
- val expectedList: List<Size> = if (Build.VERSION.SDK_INT == 21) {
- listOf(
- // Matched maximum JPEG resolution AspectRatio items, sorted by area size.
- Size(4032, 3024),
- Size(1920, 1440),
- Size(1280, 960),
- Size(640, 480),
- Size(320, 240),
- // Non-matched items have been removed by OutputSizesCorrector due to
- // TargetAspectRatio quirk.
- )
- } else {
- listOf(
- // Matched preferred AspectRatio items, sorted by area size.
- Size(3840, 2160),
- Size(1920, 1080),
- Size(1280, 720),
- Size(960, 544),
- Size(800, 450),
- Size(320, 180),
- Size(256, 144),
- // Mismatched preferred AspectRatio items, sorted by area size.
- Size(4032, 3024),
- Size(1920, 1440),
- Size(1280, 960),
- Size(640, 480),
- Size(320, 240)
- )
- }
- assertThat(resultList).isEqualTo(expectedList)
- }
-
- @Test
- fun getSupportedOutputSizes_preferredResolution1920x1080_InLimitedDevice() {
- setupCameraAndInitCameraX(
- hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
- )
- val supportedOutputSizesCollector = SupportedOutputSizesCollector(
- DEFAULT_CAMERA_ID,
- cameraCharacteristicsCompat,
- displayInfoManager
- )
- val useCase = createUseCaseByResolutionSelector(
- FAKE_USE_CASE,
- preferredResolution = Size(1920, 1080),
- sizeCoordinate = sizeCoordinate
- )
- val resultList = getSupportedOutputSizes(supportedOutputSizesCollector, useCase)
- // The 4:3 default aspect ratio will make sizes of 4/3 have the 2nd highest priority just
- // after the preferred resolution.
- val expectedList =
- listOf(
- // Matched preferred resolution size will be put in first priority.
- Size(1920, 1080),
- // Matched default preferred AspectRatio items, sorted by area size.
- Size(1920, 1440),
- Size(1280, 960),
- Size(640, 480),
- Size(320, 240),
- // Mismatched default preferred AspectRatio items, sorted by area size.
- Size(1280, 720),
- Size(960, 544),
- Size(800, 450),
- Size(320, 180),
- Size(256, 144),
- )
- assertThat(resultList).isEqualTo(expectedList)
- }
-
- @Test
- fun getSupportedOutputSizes_preferredResolution1920x1080_InLegacyDevice() {
- setupCameraAndInitCameraX()
- val supportedOutputSizesCollector = SupportedOutputSizesCollector(
- DEFAULT_CAMERA_ID,
- cameraCharacteristicsCompat,
- displayInfoManager
- )
- val useCase = createUseCaseByResolutionSelector(
- FAKE_USE_CASE,
- preferredResolution = Size(1920, 1080),
- sizeCoordinate = sizeCoordinate
- )
-
- val resultList = getSupportedOutputSizes(supportedOutputSizesCollector, useCase)
- // The 4:3 default aspect ratio will make sizes of 4/3 have the 2nd highest priority just
- // after the preferred resolution.
- val expectedList = if (Build.VERSION.SDK_INT == 21) {
- listOf(
- // Matched maximum JPEG resolution AspectRatio items, sorted by area size.
- Size(1920, 1440),
- Size(1280, 960),
- Size(640, 480),
- Size(320, 240),
- // Non-matched items have been removed by OutputSizesCorrector due to
- // TargetAspectRatio quirk.
- )
- } else {
- // The 4:3 default aspect ratio will make sizes of 4/3 have the 2nd highest
- // priority just after the preferred resolution size.
- listOf(
- // Matched default preferred resolution size will be put in first priority.
- Size(1920, 1080),
- // Matched preferred AspectRatio items, sorted by area size.
- Size(1920, 1440),
- Size(1280, 960),
- Size(640, 480),
- Size(320, 240),
- // Mismatched preferred default AspectRatio items, sorted by area size.
- Size(1280, 720),
- Size(960, 544),
- Size(800, 450),
- Size(320, 180),
- Size(256, 144),
- )
- }
- assertThat(resultList).isEqualTo(expectedList)
- }
-
- @Test
- @Suppress("DEPRECATION") /* defaultDisplay */
- fun getSupportedOutputSizes_smallDisplay_withMaxResolution1920x1080() {
- // Sets up small display.
- val windowManager = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
- Shadows.shadowOf(windowManager.defaultDisplay).setRealWidth(240)
- Shadows.shadowOf(windowManager.defaultDisplay).setRealHeight(320)
- displayInfoManager = DisplayInfoManager.getInstance(context)
-
- setupCameraAndInitCameraX(
- hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
- )
-
- val supportedOutputSizesCollector = SupportedOutputSizesCollector(
- DEFAULT_CAMERA_ID,
- cameraCharacteristicsCompat,
- displayInfoManager
- )
- val useCase = createUseCaseByResolutionSelector(
- PREVIEW_USE_CASE,
- preferredAspectRatio = AspectRatio.RATIO_16_9,
- maxResolution = Size(1920, 1080)
- )
- // Max resolution setting will remove sizes larger than 1920x1080. 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(supportedOutputSizesCollector, useCase)
- val expectedList = listOf(
- // Matched preferred AspectRatio items, sorted by area size.
- Size(1920, 1080),
- Size(1280, 720),
- Size(960, 544),
- Size(800, 450),
- Size(320, 180),
- Size(256, 144),
-
- // Mismatched preferred AspectRatio items, sorted by area size.
- Size(1280, 960),
- Size(640, 480),
- Size(320, 240)
- )
- assertThat(resultList).isEqualTo(expectedList)
- }
-
- @Test
- fun getSupportedOutputSizes_preferredResolution1800x1440NearTo4x3() {
- setupCameraAndInitCameraX(
- hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
- )
- val supportedOutputSizesCollector = SupportedOutputSizesCollector(
- DEFAULT_CAMERA_ID,
- cameraCharacteristicsCompat,
- displayInfoManager
- )
- val useCase = createUseCaseByResolutionSelector(
- FAKE_USE_CASE,
- preferredResolution = Size(1800, 1440),
- sizeCoordinate = sizeCoordinate
- )
- val resultList = getSupportedOutputSizes(supportedOutputSizesCollector, useCase)
- val expectedList =
- listOf(
- // No matched preferred resolution size found.
- // Matched default preferred AspectRatio items, sorted by area size.
- Size(1920, 1440),
- Size(1280, 960),
- Size(640, 480),
- Size(320, 240),
- // Mismatched default preferred AspectRatio items, sorted by area size.
- Size(3840, 2160),
- Size(1920, 1080),
- Size(1280, 720),
- Size(960, 544),
- Size(800, 450),
- Size(320, 180),
- Size(256, 144)
- )
- assertThat(resultList).isEqualTo(expectedList)
- }
-
- @Test
- fun getSupportedOutputSizes_preferredResolution1280x600NearTo16x9() {
- setupCameraAndInitCameraX(
- hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
- )
- val supportedOutputSizesCollector = SupportedOutputSizesCollector(
- DEFAULT_CAMERA_ID,
- cameraCharacteristicsCompat,
- displayInfoManager
- )
- val useCase = createUseCaseByResolutionSelector(
- FAKE_USE_CASE,
- preferredResolution = Size(1280, 600),
- sizeCoordinate = sizeCoordinate
- )
- val resultList = getSupportedOutputSizes(supportedOutputSizesCollector, useCase)
- val expectedList = listOf(
- // No matched preferred resolution size found.
- // Matched default preferred AspectRatio items, sorted by area size.
- Size(1280, 960),
- Size(640, 480),
- Size(320, 240),
- // Mismatched default preferred AspectRatio items, sorted by area size.
- Size(1280, 720),
- Size(960, 544),
- Size(800, 450),
- Size(320, 180),
- Size(256, 144)
- )
- assertThat(resultList).isEqualTo(expectedList)
- }
-
- @Test
- fun getSupportedOutputSizes_maxResolution1280x720() {
- setupCameraAndInitCameraX(
- hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
- )
- val supportedOutputSizesCollector = SupportedOutputSizesCollector(
- DEFAULT_CAMERA_ID,
- cameraCharacteristicsCompat,
- displayInfoManager
- )
- val useCase = createUseCaseByResolutionSelector(
- FAKE_USE_CASE,
- maxResolution = Size(1280, 720)
- )
- val resultList = getSupportedOutputSizes(supportedOutputSizesCollector, useCase)
- val expectedList = listOf(
- // Matched default preferred AspectRatio items, sorted by area size.
- Size(640, 480),
- Size(320, 240),
- // Mismatched default preferred AspectRatio items, sorted by area size.
- Size(1280, 720),
- Size(960, 544),
- Size(800, 450),
- Size(320, 180),
- Size(256, 144)
- )
- assertThat(resultList).isEqualTo(expectedList)
- }
-
- @Test
- fun getSupportedOutputSizes_maxResolution720x1280() {
- setupCameraAndInitCameraX(
- hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
- )
- val supportedOutputSizesCollector = SupportedOutputSizesCollector(
- DEFAULT_CAMERA_ID,
- cameraCharacteristicsCompat,
- displayInfoManager
- )
- val useCase = createUseCaseByResolutionSelector(
- FAKE_USE_CASE,
- maxResolution = Size(720, 1280)
- )
- val resultList = getSupportedOutputSizes(supportedOutputSizesCollector, useCase)
- val expectedList = listOf(
- // Matched default preferred AspectRatio items, sorted by area size.
- Size(640, 480),
- Size(320, 240),
- // Mismatched default preferred AspectRatio items, sorted by area size.
- Size(320, 180),
- Size(256, 144)
- )
- assertThat(resultList).isEqualTo(expectedList)
- }
-
- @Test
- fun getSupportedOutputSizes_defaultResolution1280x720_noTargetResolution() {
- setupCameraAndInitCameraX(
- hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
- )
- val supportedOutputSizesCollector = SupportedOutputSizesCollector(
- DEFAULT_CAMERA_ID,
- cameraCharacteristicsCompat,
- displayInfoManager
- )
- val useCase = createUseCaseByResolutionSelector(
- FAKE_USE_CASE,
- defaultResolution = Size(1280, 720)
- )
- val resultList = getSupportedOutputSizes(supportedOutputSizesCollector, useCase)
- val expectedList = listOf(
- // Matched default preferred AspectRatio items, sorted by area size.
- Size(1280, 960),
- Size(640, 480),
- Size(320, 240),
- // Mismatched default preferred AspectRatio items, sorted by area size.
- Size(1280, 720),
- Size(960, 544),
- Size(800, 450),
- Size(320, 180),
- Size(256, 144)
- )
- assertThat(resultList).isEqualTo(expectedList)
- }
-
- @Test
- fun getSupportedOutputSizes_defaultResolution1280x720_preferredResolution1920x1080() {
- setupCameraAndInitCameraX(
- hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
- )
- val supportedOutputSizesCollector = SupportedOutputSizesCollector(
- DEFAULT_CAMERA_ID,
- cameraCharacteristicsCompat,
- displayInfoManager
- )
- val useCase = createUseCaseByResolutionSelector(
- FAKE_USE_CASE,
- defaultResolution = Size(1280, 720),
- preferredResolution = Size(1920, 1080),
- sizeCoordinate = sizeCoordinate
- )
- val resultList = getSupportedOutputSizes(supportedOutputSizesCollector, useCase)
- val expectedList = listOf(
- // Matched preferred resolution size will be put in first priority.
- Size(1920, 1080),
- // Matched default preferred AspectRatio items, sorted by area size.
- Size(1920, 1440),
- Size(1280, 960),
- Size(640, 480),
- Size(320, 240),
- // Mismatched default preferred AspectRatio items, sorted by area size.
- Size(1280, 720),
- Size(960, 544),
- Size(800, 450),
- Size(320, 180),
- Size(256, 144)
- )
- 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 supportedOutputSizesCollector = SupportedOutputSizesCollector(
- DEFAULT_CAMERA_ID,
- cameraCharacteristicsCompat,
- displayInfoManager
- )
- val useCase = createUseCaseByResolutionSelector(
- FAKE_USE_CASE,
- preferredResolution = Size(1920, 1080),
- sizeCoordinate = sizeCoordinate
- )
- val resultList = getSupportedOutputSizes(supportedOutputSizesCollector, useCase)
- val expectedList = listOf(
- // No matched preferred resolution size found.
- // Matched default preferred AspectRatio items, sorted by area size.
- Size(640, 480),
- Size(320, 240),
- // Mismatched default preferred AspectRatio items, sorted by area size.
- 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 supportedOutputSizesCollector = SupportedOutputSizesCollector(
- DEFAULT_CAMERA_ID,
- cameraCharacteristicsCompat,
- displayInfoManager
- )
- val useCase = createUseCaseByResolutionSelector(
- FAKE_USE_CASE,
- preferredResolution = Size(320, 240),
- sizeCoordinate = sizeCoordinate,
- maxResolution = Size(320, 180)
- )
- val resultList = getSupportedOutputSizes(supportedOutputSizesCollector, useCase)
- val expectedList = listOf(Size(320, 180), Size(256, 144))
- assertThat(resultList).isEqualTo(expectedList)
- }
-
- @Test
- fun getSupportedOutputSizes_whenMaxSizeSmallerThanBigPreferredResolution() {
- setupCameraAndInitCameraX(
- hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
- )
- val supportedOutputSizesCollector = SupportedOutputSizesCollector(
- DEFAULT_CAMERA_ID,
- cameraCharacteristicsCompat,
- displayInfoManager
- )
- val useCase = createUseCaseByResolutionSelector(
- FAKE_USE_CASE,
- preferredResolution = Size(3840, 2160),
- sizeCoordinate = sizeCoordinate,
- maxResolution = Size(1920, 1080)
- )
- val resultList = getSupportedOutputSizes(supportedOutputSizesCollector, useCase)
- val expectedList = listOf(
- // No matched preferred resolution size found after filtering by max resolution setting.
- // Matched default preferred AspectRatio items, sorted by area size.
- Size(1280, 960),
- Size(640, 480),
- Size(320, 240),
- // Mismatched default preferred AspectRatio items, sorted by area size.
- Size(1920, 1080),
- Size(1280, 720),
- Size(960, 544),
- Size(800, 450),
- Size(320, 180),
- Size(256, 144)
- )
- assertThat(resultList).isEqualTo(expectedList)
- }
-
- @Test
- fun getSupportedOutputSizes_whenNoSizeBetweenMaxSizeAndPreferredResolution() {
- setupCameraAndInitCameraX(
- hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
- supportedSizes = arrayOf(
- Size(640, 480),
- Size(320, 240),
- Size(320, 180),
- Size(256, 144)
- )
- )
- val supportedOutputSizesCollector = SupportedOutputSizesCollector(
- DEFAULT_CAMERA_ID,
- cameraCharacteristicsCompat,
- displayInfoManager
- )
- val useCase = createUseCaseByResolutionSelector(
- FAKE_USE_CASE,
- preferredResolution = Size(320, 190),
- sizeCoordinate = sizeCoordinate,
- maxResolution = Size(320, 200)
- )
- val resultList = getSupportedOutputSizes(supportedOutputSizesCollector, useCase)
- val expectedList = listOf(Size(320, 180), Size(256, 144))
- assertThat(resultList).isEqualTo(expectedList)
- }
-
- @Test
- fun getSupportedOutputSizes_whenPreferredResolutionSmallerThanAnySize() {
- setupCameraAndInitCameraX(
- hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
- supportedSizes = arrayOf(
- Size(640, 480),
- Size(320, 240),
- Size(320, 180),
- Size(256, 144)
- )
- )
- val supportedOutputSizesCollector = SupportedOutputSizesCollector(
- DEFAULT_CAMERA_ID,
- cameraCharacteristicsCompat,
- displayInfoManager
- )
- val useCase = createUseCaseByResolutionSelector(
- FAKE_USE_CASE,
- preferredResolution = Size(192, 144),
- sizeCoordinate = sizeCoordinate
- )
- val resultList = getSupportedOutputSizes(supportedOutputSizesCollector, 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 supportedOutputSizesCollector = SupportedOutputSizesCollector(
- DEFAULT_CAMERA_ID,
- cameraCharacteristicsCompat,
- displayInfoManager
- )
- val useCase = createUseCaseByResolutionSelector(
- 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.
- Assert.assertThrows(IllegalArgumentException::class.java) {
- getSupportedOutputSizes(supportedOutputSizesCollector, 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 supportedOutputSizesCollector = SupportedOutputSizesCollector(
- DEFAULT_CAMERA_ID,
- cameraCharacteristicsCompat,
- displayInfoManager
- )
- val useCase = createUseCaseByResolutionSelector(
- FAKE_USE_CASE,
- preferredResolution = Size(185, 90),
- sizeCoordinate = sizeCoordinate
- )
- val resultList = getSupportedOutputSizes(supportedOutputSizesCollector, useCase)
- val expectedList = listOf(
- // No matched preferred resolution size found.
- // Matched default preferred AspectRatio items, sorted by area size.
- Size(320, 240),
- // Mismatched default preferred AspectRatio items, sorted by area size.
- Size(256, 144),
- Size(296, 144)
- )
- assertThat(resultList).isEqualTo(expectedList)
- }
-
- @Test
- fun getSupportedOutputSizes_whenOneMod16SizeClosestToTargetResolution() {
- setupCameraAndInitCameraX(
- hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
- 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 supportedOutputSizesCollector = SupportedOutputSizesCollector(
- DEFAULT_CAMERA_ID,
- cameraCharacteristicsCompat,
- displayInfoManager
- )
- val useCase = createUseCaseByResolutionSelector(
- FAKE_USE_CASE,
- preferredResolution = Size(1080, 2016),
- sizeCoordinate = sizeCoordinate
- )
- val resultList = getSupportedOutputSizes(supportedOutputSizesCollector, useCase)
- val expectedList = listOf(
- // No matched preferred resolution size found.
- // Matched default preferred AspectRatio items, sorted by area size.
- Size(1440, 1080),
- Size(1280, 960),
- Size(640, 480),
- Size(480, 360),
- Size(384, 288),
- // Mismatched default preferred AspectRatio items, sorted by area size.
- Size(1920, 1080),
- Size(1280, 720),
- Size(864, 480),
- Size(768, 432),
- Size(640, 360)
- )
- 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(960, 540),
- Size(480, 640),
- Size(640, 480),
- Size(360, 480)
- )
- )
- val supportedOutputSizesCollector = SupportedOutputSizesCollector(
- DEFAULT_CAMERA_ID,
- cameraCharacteristicsCompat,
- displayInfoManager
- )
- val useCase = createUseCaseByResolutionSelector(
- FAKE_USE_CASE,
- preferredAspectRatio = AspectRatio.RATIO_16_9
- )
- val resultList = getSupportedOutputSizes(supportedOutputSizesCollector, useCase)
- val expectedList = listOf(
- // Matched preferred AspectRatio items, sorted by area size.
- Size(1080, 1920),
- Size(720, 1280),
- // Mismatched preferred AspectRatio items, sorted by area size.
- Size(1080, 1440),
- Size(960, 1280),
- Size(480, 640),
- Size(360, 480),
- Size(640, 480),
- Size(960, 540)
- )
- 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(960, 540),
- Size(480, 640),
- Size(640, 480),
- Size(360, 480)
- )
- )
- val supportedOutputSizesCollector = SupportedOutputSizesCollector(
- DEFAULT_CAMERA_ID,
- cameraCharacteristicsCompat,
- displayInfoManager
- )
- val useCase = createUseCaseByResolutionSelector(
- FAKE_USE_CASE,
- preferredAspectRatio = AspectRatio.RATIO_16_9
- )
- // 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(supportedOutputSizesCollector, useCase)
- val expectedList = listOf(
- // Matched preferred AspectRatio items, sorted by area size.
- Size(1080, 1920),
- Size(720, 1280),
- // Mismatched preferred AspectRatio items, sorted by aspect ratio delta then area size.
- Size(1080, 1440),
- Size(960, 1280),
- Size(480, 640),
- Size(360, 480),
- Size(640, 480),
- Size(960, 540)
- )
- 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 supportedOutputSizesCollector = SupportedOutputSizesCollector(
- DEFAULT_CAMERA_ID,
- cameraCharacteristicsCompat,
- displayInfoManager
- )
- val useCase = createUseCaseByResolutionSelector(
- FAKE_USE_CASE,
- preferredAspectRatio = AspectRatio.RATIO_16_9
- )
- val resultList = getSupportedOutputSizes(supportedOutputSizesCollector, useCase)
- val expectedList = listOf(
- // Matched preferred AspectRatio items, sorted by area size.
- Size(3840, 2160),
- Size(1920, 1080),
- Size(1280, 720),
- Size(960, 544),
- Size(800, 450),
- Size(320, 180),
- Size(256, 144),
- // Mismatched preferred AspectRatio items, sorted by area size.
- Size(4032, 3024),
- Size(1920, 1440),
- Size(1280, 960),
- Size(640, 480),
- Size(320, 240)
- )
- 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(540, 960),
- Size(640, 480),
- Size(480, 640),
- Size(480, 360)
- )
- )
- val supportedOutputSizesCollector = SupportedOutputSizesCollector(
- DEFAULT_CAMERA_ID,
- cameraCharacteristicsCompat,
- displayInfoManager
- )
- val useCase = createUseCaseByResolutionSelector(
- FAKE_USE_CASE,
- preferredAspectRatio = AspectRatio.RATIO_16_9
- )
- val resultList = getSupportedOutputSizes(supportedOutputSizesCollector, useCase)
- val expectedList = listOf(
- // Matched preferred AspectRatio items, sorted by area size.
- Size(1920, 1080),
- Size(1280, 720),
- // Mismatched preferred AspectRatio items, sorted by area size.
- Size(1440, 1080),
- Size(1280, 960),
- Size(640, 480),
- Size(480, 360),
- Size(480, 640),
- Size(540, 960)
- )
- assertThat(resultList).isEqualTo(expectedList)
- }
-
- @Config(minSdk = Build.VERSION_CODES.M)
- @Test
- fun getSupportedOutputSizes_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 supportedOutputSizesCollector = SupportedOutputSizesCollector(
- DEFAULT_CAMERA_ID,
- cameraCharacteristicsCompat,
- displayInfoManager
- )
-
- val useCase = createUseCaseByResolutionSelector(
- FAKE_USE_CASE,
- preferredAspectRatio = AspectRatio.RATIO_16_9,
- highResolutionEnabled = true
- )
- val resultList = getSupportedOutputSizes(supportedOutputSizesCollector, useCase)
- val expectedList = listOf(
- // Matched preferred AspectRatio items, sorted by area size.
- Size(8000, 4500),
- Size(3840, 2160),
- Size(1920, 1080),
- Size(1280, 720),
- Size(960, 544),
- Size(800, 450),
- Size(320, 180),
- Size(256, 144),
- // Mismatched preferred AspectRatio items, sorted by area size.
- Size(8000, 6000),
- Size(4032, 3024),
- Size(1920, 1440),
- Size(1280, 960),
- Size(640, 480),
- Size(320, 240)
- )
- assertThat(resultList).isEqualTo(expectedList)
- }
-
- @Config(minSdk = Build.VERSION_CODES.M)
- @Test
- fun highResolutionCanNotBeSelected_whenHighResolutionForceDisabled() {
- 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 supportedOutputSizesCollector = SupportedOutputSizesCollector(
- DEFAULT_CAMERA_ID,
- cameraCharacteristicsCompat,
- displayInfoManager
- )
-
- val useCase = createUseCaseByResolutionSelector(
- FAKE_USE_CASE,
- preferredAspectRatio = AspectRatio.RATIO_16_9,
- highResolutionEnabled = true,
- highResolutionForceDisabled = true
- )
- val resultList = getSupportedOutputSizes(supportedOutputSizesCollector, useCase)
- val expectedList = listOf(
- // Matched preferred AspectRatio items, sorted by area size.
- Size(3840, 2160),
- Size(1920, 1080),
- Size(1280, 720),
- Size(960, 544),
- Size(800, 450),
- Size(320, 180),
- Size(256, 144),
- // Mismatched preferred AspectRatio items, sorted by area size.
- Size(4032, 3024),
- Size(1920, 1440),
- Size(1280, 960),
- Size(640, 480),
- Size(320, 240)
- )
- assertThat(resultList).isEqualTo(expectedList)
- }
-
- /**
- * 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
- )
-
- @CameraSelector.LensFacing val lensFacingEnum = CameraUtil.getLensFacingEnumFromInt(
- CameraCharacteristics.LENS_FACING_BACK
- )
- cameraManagerCompat = CameraManagerCompat.from(context)
- val cameraInfo = FakeCameraInfoInternal(
- cameraId,
- sensorOrientation,
- CameraCharacteristics.LENS_FACING_BACK
- )
-
- cameraFactory = FakeCameraFactory().apply {
- insertCamera(lensFacingEnum, cameraId) {
- FakeCamera(cameraId, null, cameraInfo)
- }
- }
-
- cameraCharacteristicsCompat = cameraManagerCompat.getCameraCharacteristicsCompat(cameraId)
-
- initCameraX()
- }
-
- /**
- * Initializes the [CameraX].
- */
- private fun initCameraX() {
- val surfaceManagerProvider =
- CameraDeviceSurfaceManager.Provider { context, _, availableCameraIds ->
- Camera2DeviceSurfaceManager(
- context,
- mockCamcorderProfileHelper,
- CameraManagerCompat.from(this@SupportedOutputSizesCollectorTest.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
- }
-
- /**
- * 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(
- supportedOutputSizesCollector: SupportedOutputSizesCollector,
- useCase: UseCase,
- cameraId: String = DEFAULT_CAMERA_ID,
- sensorOrientation: Int = SENSOR_ORIENTATION_90,
- 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
- )
-
- val useCaseConfig = useCaseToConfigMap[useCase]!!
- val resolutionSelector = (useCaseConfig as ImageOutputConfig).resolutionSelector
- val imageFormat = useCaseConfig.inputFormat
- val isHighResolutionDisabled = useCaseConfig.isHigResolutionDisabled(false)
- val customizedSupportSizes = getCustomizedSupportSizesFromConfig(imageFormat, useCaseConfig)
- val miniBoundingSize = SupportedOutputSizesCollector.getTargetSizeByResolutionSelector(
- resolutionSelector,
- Surface.ROTATION_0,
- sensorOrientation,
- CameraCharacteristics.LENS_FACING_BACK
- ) ?: useCaseConfig.getDefaultResolution(null)
-
- return supportedOutputSizesCollector.getSupportedOutputSizes(
- resolutionSelector,
- imageFormat,
- miniBoundingSize,
- isHighResolutionDisabled,
- customizedSupportSizes
- )
- }
-
- companion object {
-
- /**
- * Sets up camera according to the specified settings.
- *
- * @param cameraId the camera id to be set up. Default value is [DEFAULT_CAMERA_ID].
- * @param hardwareLevel the hardware level of the camera. Default value is
- * [CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY].
- * @param sensorOrientation the sensor orientation of the camera. Default value is
- * [SENSOR_ORIENTATION_90].
- * @param pixelArraySize the active pixel array size of the camera. Default value is
- * [LANDSCAPE_PIXEL_ARRAY_SIZE].
- * @param supportedSizes the supported sizes of the camera. Default value is
- * [DEFAULT_SUPPORTED_SIZES].
- * @param capabilities the capabilities of the camera. Default value is null.
- */
- @JvmStatic
- fun setupCamera(
- cameraId: String = DEFAULT_CAMERA_ID,
- hardwareLevel: Int = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY,
- sensorOrientation: Int = SENSOR_ORIENTATION_90,
- pixelArraySize: Size = LANDSCAPE_PIXEL_ARRAY_SIZE,
- supportedSizes: Array<Size> = DEFAULT_SUPPORTED_SIZES,
- supportedHighResolutionSizes: Array<Size>? = null,
- capabilities: IntArray? = null
- ) {
- val mockMap = Mockito.mock(StreamConfigurationMap::class.java).also {
- // Sets up the supported sizes
- Mockito.`when`(it.getOutputSizes(ArgumentMatchers.anyInt()))
- .thenReturn(supportedSizes)
- // ImageFormat.PRIVATE was supported since API level 23. Before that, the supported
- // output sizes need to be retrieved via SurfaceTexture.class.
- Mockito.`when`(it.getOutputSizes(SurfaceTexture::class.java))
- .thenReturn(supportedSizes)
- // This is setup for the test to determine RECORD size from StreamConfigurationMap
- Mockito.`when`(it.getOutputSizes(MediaRecorder::class.java))
- .thenReturn(supportedSizes)
-
- // setup to return different minimum frame durations depending on resolution
- // minimum frame durations were designated only for the purpose of testing
- Mockito.`when`(it.getOutputMinFrameDuration(anyInt(), eq(Size(4032, 3024))))
- .thenReturn(50000000L) // 20 fps, size maximum
-
- Mockito.`when`(it.getOutputMinFrameDuration(anyInt(), eq(Size(3840, 2160))))
- .thenReturn(40000000L) // 25, size record
-
- Mockito.`when`(it.getOutputMinFrameDuration(anyInt(), eq(Size(1920, 1440))))
- .thenReturn(30000000L) // 30
-
- Mockito.`when`(it.getOutputMinFrameDuration(anyInt(), eq(Size(1920, 1080))))
- .thenReturn(28000000L) // 35
-
- Mockito.`when`(it.getOutputMinFrameDuration(anyInt(), eq(Size(1280, 960))))
- .thenReturn(25000000L) // 40
-
- Mockito.`when`(it.getOutputMinFrameDuration(anyInt(), eq(Size(1280, 720))))
- .thenReturn(22000000L) // 45, size preview/display
-
- Mockito.`when`(it.getOutputMinFrameDuration(anyInt(), eq(Size(960, 544))))
- .thenReturn(20000000L) // 50
-
- Mockito.`when`(it.getOutputMinFrameDuration(anyInt(), eq(Size(800, 450))))
- .thenReturn(16666000L) // 60fps
-
- Mockito.`when`(it.getOutputMinFrameDuration(anyInt(), eq(Size(640, 480))))
- .thenReturn(16666000L) // 60fps
-
- // Sets up the supported high resolution sizes
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
- Mockito.`when`(it.getHighResolutionOutputSizes(ArgumentMatchers.anyInt()))
- .thenReturn(supportedHighResolutionSizes)
- }
- }
-
- val characteristics = ShadowCameraCharacteristics.newCameraCharacteristics()
- Shadow.extract<ShadowCameraCharacteristics>(characteristics).apply {
- set(CameraCharacteristics.LENS_FACING, CameraCharacteristics.LENS_FACING_BACK)
- set(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL, hardwareLevel)
- set(CameraCharacteristics.SENSOR_ORIENTATION, sensorOrientation)
- set(CameraCharacteristics.SENSOR_INFO_PIXEL_ARRAY_SIZE, pixelArraySize)
- set(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP, mockMap)
- capabilities?.let {
- set(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES, it)
- }
- }
-
- val cameraManager = ApplicationProvider.getApplicationContext<Context>()
- .getSystemService(Context.CAMERA_SERVICE) as CameraManager
- (Shadow.extract<Any>(cameraManager) as ShadowCameraManager)
- .addCamera(cameraId, characteristics)
- }
-
- /**
- * Creates [Preview], [ImageCapture], [ImageAnalysis], [androidx.camera.core.VideoCapture] or
- * FakeUseCase by the legacy or new ResolutionSelector API according to the specified settings.
- *
- * @param useCaseType Which of [Preview], [ImageCapture], [ImageAnalysis],
- * [androidx.camera.core.VideoCapture] and FakeUseCase should be created.
- * @param preferredAspectRatio the target aspect ratio setting. Default is UNKNOWN_ASPECT_RATIO
- * and no target aspect ratio will be set to the created use case.
- * @param preferredResolution the preferred resolution setting which should be specified in the
- * camera sensor coordinate. The resolution will be transformed to set via
- * [ResolutionSelector.Builder.setPreferredResolutionByViewSize] if size coordinate is
- * [SizeCoordinate.ANDROID_VIEW]. Default is null.
- * @param maxResolution the max resolution setting. Default is null.
- * @param highResolutionEnabled the high resolution setting, Default is false.
- * @param highResolutionForceDisabled the high resolution force disabled setting, Default
- * is false. This will be set in the use case config to force disable high resolution.
- * @param defaultResolution the default resolution setting. Default is null.
- * @param supportedResolutions the customized supported resolutions. Default is null.
- */
- @JvmStatic
- fun createUseCaseByResolutionSelector(
- useCaseType: Int,
- preferredAspectRatio: Int = UNKNOWN_ASPECT_RATIO,
- preferredResolution: Size? = null,
- sizeCoordinate: SizeCoordinate = SizeCoordinate.CAMERA_SENSOR,
- maxResolution: Size? = null,
- highResolutionEnabled: Boolean = false,
- highResolutionForceDisabled: Boolean = false,
- defaultResolution: Size? = null,
- supportedResolutions: List<Pair<Int, Array<Size>>>? = null,
- customOrderedResolutions: List<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)
- }
-
- val resolutionSelectorBuilder = ResolutionSelector.Builder()
-
- if (preferredAspectRatio != UNKNOWN_ASPECT_RATIO) {
- resolutionSelectorBuilder.setPreferredAspectRatio(preferredAspectRatio)
- }
-
- preferredResolution?.let {
- if (sizeCoordinate == SizeCoordinate.CAMERA_SENSOR) {
- resolutionSelectorBuilder.setPreferredResolution(it)
- } else {
- val flippedResolution = Size(
- /* width= */ it.height,
- /* height= */ it.width
- )
- resolutionSelectorBuilder.setPreferredResolutionByViewSize(flippedResolution)
- }
- }
-
- maxResolution?.let { resolutionSelectorBuilder.setMaxResolution(it) }
- resolutionSelectorBuilder.setHighResolutionEnabled(highResolutionEnabled)
-
- builder.setResolutionSelector(resolutionSelectorBuilder.build())
- builder.setHighResolutionDisabled(highResolutionForceDisabled)
-
- defaultResolution?.let { builder.setDefaultResolution(it) }
- supportedResolutions?.let { builder.setSupportedResolutions(it) }
- customOrderedResolutions?.let { builder.setCustomOrderedResolutions(it) }
- return builder.build()
- }
-
- @JvmStatic
- fun getCustomizedSupportSizesFromConfig(
- imageFormat: Int,
- config: ImageOutputConfig
- ): Array<Size>? {
- var outputSizes: Array<Size>? = null
-
- // Try to retrieve customized supported resolutions from config.
- val formatResolutionsPairList = config.getSupportedResolutions(null)
- if (formatResolutionsPairList != null) {
- for (formatResolutionPair in formatResolutionsPairList) {
- if (formatResolutionPair.first == imageFormat) {
- outputSizes = formatResolutionPair.second
- break
- }
- }
- }
- return outputSizes
- }
-
- @JvmStatic
- @ParameterizedRobolectricTestRunner.Parameters(name = "sizeCoordinate = {0}")
- fun data() = listOf(
- SizeCoordinate.CAMERA_SENSOR,
- SizeCoordinate.ANDROID_VIEW
- )
- }
-}
\ No newline at end of file
diff --git a/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/SupportedSizeConstraintsTest.java b/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/SupportedSizeConstraintsTest.java
index 3ed7e19..7505ee1 100644
--- a/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/SupportedSizeConstraintsTest.java
+++ b/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/SupportedSizeConstraintsTest.java
@@ -35,7 +35,9 @@
import androidx.annotation.NonNull;
import androidx.camera.camera2.Camera2Config;
+import androidx.camera.camera2.internal.compat.CameraCharacteristicsCompat;
import androidx.camera.camera2.internal.compat.CameraManagerCompat;
+import androidx.camera.camera2.internal.compat.StreamConfigurationMapCompat;
import androidx.camera.camera2.internal.compat.workaround.ExcludedSupportedSizesContainer;
import androidx.camera.core.CameraUnavailableException;
import androidx.camera.core.CameraXConfig;
@@ -45,8 +47,6 @@
import androidx.camera.testing.CameraXUtil;
import androidx.camera.testing.fakes.FakeCamera;
import androidx.camera.testing.fakes.FakeCameraFactory;
-import androidx.camera.testing.fakes.FakeUseCase;
-import androidx.camera.testing.fakes.FakeUseCaseConfig;
import androidx.test.core.app.ApplicationProvider;
import org.codehaus.plexus.util.ReflectionUtils;
@@ -152,12 +152,13 @@
// mSupportedSizes modified unexpectedly.
assertThat(Arrays.asList(mSupportedSizes)).containsAtLeastElementsIn(excludedSizes);
- // Make the fake use case have JPEG format since those sizes are excluded for JPEG format.
- final FakeUseCase useCase = new FakeUseCaseConfig.Builder()
- .setBufferFormat(ImageFormat.JPEG)
- .build();
- final List<Size> resultList = supportedSurfaceCombination.getSupportedOutputSizes(
- useCase.getCurrentConfig());
+ // These sizes should be excluded when retrieving output sizes via
+ // StreamConfigurationMapCompat#getOutputSizes()
+ CameraCharacteristicsCompat characteristicsCompat =
+ mCameraManagerCompat.getCameraCharacteristicsCompat(BACK_CAMERA_ID);
+ StreamConfigurationMapCompat mapCompat =
+ characteristicsCompat.getStreamConfigurationMapCompat();
+ final List<Size> resultList = Arrays.asList(mapCompat.getOutputSizes(ImageFormat.JPEG));
assertThat(resultList).containsNoneIn(excludedSizes);
}
diff --git a/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/SupportedSurfaceCombinationTest.kt b/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/SupportedSurfaceCombinationTest.kt
index 51f2a79..ed4755d 100644
--- a/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/SupportedSurfaceCombinationTest.kt
+++ b/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/SupportedSurfaceCombinationTest.kt
@@ -1,4 +1,3 @@
-
/*
* Copyright 2022 The Android Open Source Project
*
@@ -20,78 +19,50 @@
import android.content.Context
import android.content.pm.PackageManager.FEATURE_CAMERA_CONCURRENT
import android.graphics.ImageFormat
+import android.graphics.SurfaceTexture
import android.hardware.camera2.CameraCharacteristics
+import android.hardware.camera2.CameraManager
import android.hardware.camera2.CameraMetadata
+import android.hardware.camera2.params.StreamConfigurationMap
import android.media.CamcorderProfile
import android.media.CamcorderProfile.QUALITY_1080P
import android.media.CamcorderProfile.QUALITY_2160P
import android.media.CamcorderProfile.QUALITY_480P
import android.media.CamcorderProfile.QUALITY_720P
+import android.media.MediaRecorder
import android.os.Build
-import android.util.Pair
import android.util.Range
-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.CameraAccessExceptionCompat
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.AttachedSurfaceInfo
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.StreamSpec
+import androidx.camera.core.impl.ImageFormatConstants.INTERNAL_DEFINED_IMAGE_FORMAT_PRIVATE
import androidx.camera.core.impl.SurfaceCombination
import androidx.camera.core.impl.SurfaceConfig
import androidx.camera.core.impl.SurfaceConfig.ConfigSize
import androidx.camera.core.impl.SurfaceConfig.ConfigType
import androidx.camera.core.impl.UseCaseConfig
-import androidx.camera.core.impl.UseCaseConfig.OPTION_TARGET_FRAME_RATE
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.impl.UseCaseConfigFactory.CaptureType
import androidx.camera.core.internal.utils.SizeUtil.RESOLUTION_1440P
import androidx.camera.core.internal.utils.SizeUtil.RESOLUTION_720P
import androidx.camera.core.internal.utils.SizeUtil.RESOLUTION_VGA
import androidx.camera.testing.CameraUtil
import androidx.camera.testing.CameraXUtil
-import androidx.camera.testing.Configs
import androidx.camera.testing.EncoderProfilesUtil
-import androidx.camera.testing.SurfaceTextureProvider
-import androidx.camera.testing.SurfaceTextureProvider.SurfaceTextureCallback
import androidx.camera.testing.fakes.FakeCamera
import androidx.camera.testing.fakes.FakeCameraFactory
import androidx.camera.testing.fakes.FakeCameraInfoInternal
import androidx.camera.testing.fakes.FakeEncoderProfilesProvider
import androidx.camera.testing.fakes.FakeUseCaseConfig
-import androidx.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
@@ -103,30 +74,22 @@
import org.mockito.ArgumentMatchers
import org.mockito.Mockito
import org.robolectric.RobolectricTestRunner
-import org.robolectric.Shadows
import org.robolectric.Shadows.shadowOf
import org.robolectric.annotation.Config
import org.robolectric.annotation.internal.DoNotInstrument
+import org.robolectric.shadow.api.Shadow
+import org.robolectric.shadows.ShadowCameraCharacteristics
+import org.robolectric.shadows.ShadowCameraManager
-private const val 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
@@ -137,9 +100,6 @@
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 */
@@ -169,101 +129,15 @@
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?,
- targetFrameRate: Range<Int>?,
- surfaceOccupancyPriority: Int,
- maxResolution: Size?,
- highResolutionEnabled: Boolean,
- defaultResolution: Size?,
- supportedResolutions: List<Pair<Int, Array<Size>>>?,
- customOrderedResolutions: List<Size>?,
- ): UseCase {
- return createUseCaseByLegacyApi(
- useCaseType,
- targetRotation,
- preferredAspectRatio,
- preferredResolution,
- targetFrameRate,
- surfaceOccupancyPriority,
- maxResolution,
- defaultResolution,
- supportedResolutions,
- customOrderedResolutions,
- )
- }
- }
-
- private val resolutionSelectorUseCaseCreator = object : UseCaseCreator {
- override fun createUseCase(
- useCaseType: Int,
- targetRotation: Int,
- preferredAspectRatio: Int,
- preferredResolution: Size?,
- targetFrameRate: Range<Int>?,
- surfaceOccupancyPriority: Int,
- maxResolution: Size?,
- highResolutionEnabled: Boolean,
- defaultResolution: Size?,
- supportedResolutions: List<Pair<Int, Array<Size>>>?,
- customOrderedResolutions: List<Size>?,
- ): UseCase {
- return createUseCaseByResolutionSelector(
- useCaseType,
- preferredAspectRatio,
- preferredResolution,
- sizeCoordinate = SizeCoordinate.CAMERA_SENSOR,
- maxResolution,
- highResolutionEnabled,
- highResolutionForceDisabled = false,
- defaultResolution,
- supportedResolutions,
- customOrderedResolutions,
- )
- }
- }
-
- private val viewSizeResolutionSelectorUseCaseCreator = object : UseCaseCreator {
- override fun createUseCase(
- useCaseType: Int,
- targetRotation: Int,
- preferredAspectRatio: Int,
- preferredResolution: Size?,
- targetFrameRate: Range<Int>?,
- surfaceOccupancyPriority: Int,
- maxResolution: Size?,
- highResolutionEnabled: Boolean,
- defaultResolution: Size?,
- supportedResolutions: List<Pair<Int, Array<Size>>>?,
- customOrderedResolutions: List<Size>?,
- ): UseCase {
- return createUseCaseByResolutionSelector(
- useCaseType,
- preferredAspectRatio,
- preferredResolution,
- sizeCoordinate = SizeCoordinate.ANDROID_VIEW,
- maxResolution,
- highResolutionEnabled,
- highResolutionForceDisabled = false,
- defaultResolution,
- supportedResolutions,
- customOrderedResolutions
- )
- }
- }
+ private lateinit var cameraDeviceSurfaceManager: CameraDeviceSurfaceManager
@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)
+ shadowOf(windowManager.defaultDisplay).setRealWidth(DISPLAY_SIZE.width)
+ shadowOf(windowManager.defaultDisplay).setRealHeight(DISPLAY_SIZE.height)
Mockito.`when`(
mockCamcorderProfileHelper.hasProfile(
ArgumentMatchers.anyInt(),
@@ -282,6 +156,12 @@
CameraXUtil.shutdown()[10000, TimeUnit.MILLISECONDS]
}
+ // //////////////////////////////////////////////////////////////////////////////////////////
+ //
+ // Surface combination support tests for guaranteed configurations
+ //
+ // //////////////////////////////////////////////////////////////////////////////////////////
+
@Test
fun checkLegacySurfaceCombinationSupportedInLegacyDevice() {
setupCameraAndInitCameraX()
@@ -556,731 +436,11 @@
}
}
- @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 suggestedStreamSpecMap = getSuggestedStreamSpecMap(
- false, supportedSurfaceCombination, useCase)
- val selectedSize = suggestedStreamSpecMap[useCase]!!.resolution
- 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 suggestedStreamSpecMap = getSuggestedStreamSpecMap(
- false,
- supportedSurfaceCombination,
- preview,
- imageCapture, imageAnalysis
- )
- val previewResolution = suggestedStreamSpecMap[preview]!!.resolution
- val imageCaptureResolution = suggestedStreamSpecMap[imageCapture]!!.resolution
- val imageAnalysisResolution = suggestedStreamSpecMap[imageAnalysis]!!.resolution
- // 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 suggestedStreamSpecMap = getSuggestedStreamSpecMap(
- false,
- supportedSurfaceCombination, preview,
- imageCapture, imageAnalysis
- )
- val previewSize = suggestedStreamSpecMap[preview]!!.resolution
- val imageCaptureSize = suggestedStreamSpecMap[imageCapture]!!.resolution
- val imageAnalysisSize = suggestedStreamSpecMap[imageAnalysis]!!.resolution
-
- 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 suggestedStreamSpecMap = getSuggestedStreamSpecMap(
- false,
- 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 = suggestedStreamSpecMap[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 suggestedStreamSpecMap =
- getSuggestedStreamSpecMap(
- false, supportedSurfaceCombination, imageCapture)
- assertThat(it).isEqualTo(suggestedStreamSpecMap[imageCapture]!!.resolution)
- }
- }
-
- @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 suggestedStreamSpecMap = getSuggestedStreamSpecMap(
- false, supportedSurfaceCombination, useCase)
- assertThat(suggestedStreamSpecMap[useCase]!!.resolution).isEqualTo(expectedResult)
- }
-
- @Test
- fun suggestedStreamSpecsForMixedUseCaseNotSupportedInLegacyDevice_LegacyApi() {
- suggestedStreamSpecsForMixedUseCaseNotSupportedInLegacyDevice(legacyUseCaseCreator)
- }
-
- @Test
- fun suggestedStreamSpecsForMixedUseCaseNotSupportedInLegacyDevice_ResolutionSelector() {
- suggestedStreamSpecsForMixedUseCaseNotSupportedInLegacyDevice(
- resolutionSelectorUseCaseCreator
- )
- }
-
- private fun suggestedStreamSpecsForMixedUseCaseNotSupportedInLegacyDevice(
- 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) {
- getSuggestedStreamSpecMap(
- false,
- supportedSurfaceCombination,
- imageCapture,
- videoCapture,
- preview
- )
- }
- }
-
- @Test
- fun suggestedStreamSpecsForCustomizeResolutionsNotSupportedInLegacyDevice_LegacyApi() {
- suggestedStreamSpecsForCustomizeResolutionsNotSupportedInLegacyDevice(legacyUseCaseCreator)
- }
-
- @Test
- fun suggestedStreamSpecsForCustomizeResolutionsNotSupportedInLegacyDevice_ResolutionSelector() {
- suggestedStreamSpecsForCustomizeResolutionsNotSupportedInLegacyDevice(
- resolutionSelectorUseCaseCreator
- )
- }
-
- private fun suggestedStreamSpecsForCustomizeResolutionsNotSupportedInLegacyDevice(
- 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) {
- getSuggestedStreamSpecMap(
- false, supportedSurfaceCombination, videoCapture, preview)
- }
- }
-
- @Test
- fun getsuggestedStreamSpecsForMixedUseCaseInLimitedDevice_LegacyApi() {
- getsuggestedStreamSpecsForMixedUseCaseInLimitedDevice(legacyUseCaseCreator)
- }
-
- @Test
- fun getsuggestedStreamSpecsForMixedUseCaseInLimitedDevice_ResolutionSelector() {
- getsuggestedStreamSpecsForMixedUseCaseInLimitedDevice(resolutionSelectorUseCaseCreator)
- }
-
- private fun getsuggestedStreamSpecsForMixedUseCaseInLimitedDevice(
- 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 suggestedStreamSpecMap = getSuggestedStreamSpecMap(
- false,
- supportedSurfaceCombination,
- imageCapture,
- videoCapture,
- preview
- )
- // (PRIV, PREVIEW) + (PRIV, RECORD) + (JPEG, RECORD)
- assertThat(suggestedStreamSpecMap[imageCapture]!!.resolution).isEqualTo(RECORD_SIZE)
- assertThat(suggestedStreamSpecMap[videoCapture]!!.resolution).isEqualTo(RECORD_SIZE)
- assertThat(suggestedStreamSpecMap[preview]!!.resolution).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 getsuggestedStreamSpecsInFullDevice_videoHasHigherPriorityThanImage_LegacyApi() {
- getsuggestedStreamSpecsInFullDevice_videoHasHigherPriorityThanImage(
- legacyUseCaseCreator)
- }
-
- @Test
- @Throws(CameraUnavailableException::class)
- fun getsuggestedStreamSpecsInFullDevice_videoHasHigherPriorityThanImage_ResolutionSelector() {
- getsuggestedStreamSpecsInFullDevice_videoHasHigherPriorityThanImage(
- resolutionSelectorUseCaseCreator
- )
- }
-
- private fun getsuggestedStreamSpecsInFullDevice_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 suggestedStreamSpecMap = getSuggestedStreamSpecMap(
- false,
- 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(suggestedStreamSpecMap[imageCapture]!!.resolution).isEqualTo(RECORD_SIZE)
- assertThat(suggestedStreamSpecMap[videoCapture]!!.resolution).isEqualTo(RECORD_SIZE)
- assertThat(suggestedStreamSpecMap[preview]!!.resolution).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 suggestedStreamSpecMap = getSuggestedStreamSpecMap(
- false,
- 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(suggestedStreamSpecMap[imageCapture]!!.resolution).isEqualTo(MAXIMUM_SIZE)
- // Quality.HD
- assertThat(suggestedStreamSpecMap[videoCapture]!!.resolution).isEqualTo(PREVIEW_SIZE)
- assertThat(suggestedStreamSpecMap[preview]!!.resolution).isEqualTo(PREVIEW_SIZE)
- }
-
- @Test
- fun getsuggestedStreamSpecsWithSameSupportedListForDifferentUseCases_LegacyApi() {
- getsuggestedStreamSpecsWithSameSupportedListForDifferentUseCases(
- legacyUseCaseCreator,
- DISPLAY_SIZE
- )
- }
-
- @Test
- fun getsuggestedStreamSpecsWithSameSupportedListForDifferentUseCases_RS_SensorSize() {
- getsuggestedStreamSpecsWithSameSupportedListForDifferentUseCases(
- resolutionSelectorUseCaseCreator,
- PREVIEW_SIZE
- )
- }
-
- @Test
- fun getsuggestedStreamSpecsWithSameSupportedListForDifferentUseCases_RS_ViewSize() {
- getsuggestedStreamSpecsWithSameSupportedListForDifferentUseCases(
- viewSizeResolutionSelectorUseCaseCreator,
- PREVIEW_SIZE
- )
- }
-
- private fun getsuggestedStreamSpecsWithSameSupportedListForDifferentUseCases(
- 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 suggestedStreamSpecMap = getSuggestedStreamSpecMap(
- false,
- supportedSurfaceCombination,
- imageCapture,
- imageAnalysis,
- preview
- )
- assertThat(suggestedStreamSpecMap[imageCapture]!!.resolution).isEqualTo(PREVIEW_SIZE)
- assertThat(suggestedStreamSpecMap[imageAnalysis]!!.resolution).isEqualTo(PREVIEW_SIZE)
- assertThat(suggestedStreamSpecMap[preview]!!.resolution).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 suggestedStreamSpecMap = getSuggestedStreamSpecMap(
- false,
- supportedSurfaceCombination,
- preview,
- imageCapture,
- imageAnalysis
- )
- assertThat(
- hasMatchingAspectRatio(
- suggestedStreamSpecMap[preview]!!.resolution,
- ASPECT_RATIO_16_9
- )
- ).isTrue()
- assertThat(
- hasMatchingAspectRatio(
- suggestedStreamSpecMap[imageCapture]!!.resolution,
- ASPECT_RATIO_16_9
- )
- ).isTrue()
- assertThat(
- hasMatchingAspectRatio(
- suggestedStreamSpecMap[imageAnalysis]!!.resolution,
- ASPECT_RATIO_16_9
- )
- ).isTrue()
- }
-
- @Test
- fun getsuggestedStreamSpecsForCustomizedSupportedResolutions_LegacyApi() {
- getsuggestedStreamSpecsForCustomizedSupportedResolutions(legacyUseCaseCreator)
- }
-
- @Test
- fun getsuggestedStreamSpecsForCustomizedSupportedResolutions_ResolutionSelector() {
- getsuggestedStreamSpecsForCustomizedSupportedResolutions(resolutionSelectorUseCaseCreator)
- }
-
- private fun getsuggestedStreamSpecsForCustomizedSupportedResolutions(
- 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 suggestedStreamSpecMap = getSuggestedStreamSpecMap(
- false,
- supportedSurfaceCombination,
- imageCapture,
- videoCapture,
- preview
- )
- // Checks all resolutions in suggested stream specs will become 640x480.
- assertThat(suggestedStreamSpecMap[imageCapture]?.resolution).isEqualTo(RESOLUTION_VGA)
- assertThat(suggestedStreamSpecMap[videoCapture]?.resolution).isEqualTo(RESOLUTION_VGA)
- assertThat(suggestedStreamSpecMap[preview]?.resolution).isEqualTo(RESOLUTION_VGA)
- }
+ // //////////////////////////////////////////////////////////////////////////////////////////
+ //
+ // Surface config transformation tests
+ //
+ // //////////////////////////////////////////////////////////////////////////////////////////
@Test
fun transformSurfaceConfigWithYUVAnalysisSize() {
@@ -1484,6 +644,947 @@
assertThat(surfaceConfig).isEqualTo(expectedSurfaceConfig)
}
+ // //////////////////////////////////////////////////////////////////////////////////////////
+ //
+ // Resolution selection tests for LEGACY-level guaranteed configurations
+ //
+ // //////////////////////////////////////////////////////////////////////////////////////////
+
+ /**
+ * PRIV/MAXIMUM
+ */
+ @Test
+ fun canSelectCorrectSize_singlePrivStream_inLegacyDevice() {
+ val privUseCase = createUseCase(CaptureType.VIDEO_CAPTURE)
+ val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+ put(privUseCase, MAXIMUM_SIZE)
+ }
+ getSuggestedSpecsAndVerify(useCaseExpectedResultMap)
+ }
+
+ /**
+ * JPEG/MAXIMUM
+ */
+ @Test
+ fun canSelectCorrectSize_singleJpegStream_inLegacyDevice() {
+ val jpegUseCase = createUseCase(CaptureType.IMAGE_CAPTURE)
+ val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+ put(jpegUseCase, MAXIMUM_SIZE)
+ }
+ getSuggestedSpecsAndVerify(useCaseExpectedResultMap)
+ }
+
+ /**
+ * YUV/MAXIMUM
+ */
+ @Test
+ fun canSelectCorrectSize_singleYuvStream_inLegacyDevice() {
+ val yuvUseCase = createUseCase(CaptureType.IMAGE_ANALYSIS)
+ val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+ put(yuvUseCase, MAXIMUM_SIZE)
+ }
+ getSuggestedSpecsAndVerify(useCaseExpectedResultMap)
+ }
+
+ /**
+ * PRIV/PREVIEW + JPEG/MAXIMUM
+ */
+ @Test
+ fun canSelectCorrectSizes_privPlusJpeg_inLegacyDevice() {
+ val privUseCase = createUseCase(CaptureType.PREVIEW)
+ val jpegUseCase = createUseCase(CaptureType.IMAGE_CAPTURE)
+ val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+ put(privUseCase, PREVIEW_SIZE)
+ put(jpegUseCase, MAXIMUM_SIZE)
+ }
+ getSuggestedSpecsAndVerify(useCaseExpectedResultMap)
+ }
+
+ /**
+ * YUV/PREVIEW + JPEG/MAXIMUM
+ */
+ @Test
+ fun canSelectCorrectSizes_yuvPlusJpeg_inLegacyDevice() {
+ val yuvUseCase = createUseCase(CaptureType.IMAGE_ANALYSIS) // YUV
+ val jpegUseCase = createUseCase(CaptureType.IMAGE_CAPTURE) // JPEG
+ val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+ put(yuvUseCase, PREVIEW_SIZE)
+ put(jpegUseCase, MAXIMUM_SIZE)
+ }
+ getSuggestedSpecsAndVerify(useCaseExpectedResultMap)
+ }
+
+ /**
+ * PRIV/PREVIEW + PRIV/PREVIEW
+ */
+ @Test
+ fun canSelectCorrectSizes_privPlusPriv_inLegacyDevice() {
+ val privUseCase1 = createUseCase(CaptureType.PREVIEW) // PRIV
+ val privUseCase2 = createUseCase(CaptureType.VIDEO_CAPTURE) // PRIV
+ val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+ put(privUseCase1, PREVIEW_SIZE)
+ put(privUseCase2, PREVIEW_SIZE)
+ }
+ getSuggestedSpecsAndVerify(useCaseExpectedResultMap)
+ }
+
+ /**
+ * PRIV/PREVIEW + YUV/PREVIEW
+ */
+ @Test
+ fun canSelectCorrectSizes_privPlusYuv_inLegacyDevice() {
+ val privUseCase = createUseCase(CaptureType.PREVIEW) // PRIV
+ val yuvUseCase = createUseCase(CaptureType.IMAGE_ANALYSIS) // YUV
+ val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+ put(privUseCase, PREVIEW_SIZE)
+ put(yuvUseCase, PREVIEW_SIZE)
+ }
+ getSuggestedSpecsAndVerify(useCaseExpectedResultMap)
+ }
+
+ /**
+ * PRIV/PREVIEW + YUV/PREVIEW + JPEG/MAXIMUM
+ */
+ @Test
+ fun canSelectCorrectSizes_privPlusYuvPlusJpeg_inLegacyDevice() {
+ val privUseCase = createUseCase(CaptureType.PREVIEW) // PRIV
+ val yuvUseCase = createUseCase(CaptureType.IMAGE_ANALYSIS) // YUV
+ val jpegUseCase = createUseCase(CaptureType.IMAGE_CAPTURE) // JPEG
+ val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+ put(privUseCase, PREVIEW_SIZE)
+ put(yuvUseCase, PREVIEW_SIZE)
+ put(jpegUseCase, MAXIMUM_SIZE)
+ }
+ getSuggestedSpecsAndVerify(useCaseExpectedResultMap)
+ }
+
+ /**
+ * Unsupported PRIV + JPEG + PRIV for legacy level devices
+ */
+ @Test
+ fun throwsException_unsupportedConfiguration_inLegacyDevice() {
+ val privUseCase1 = createUseCase(CaptureType.PREVIEW) // PRIV
+ val jpegUseCase = createUseCase(CaptureType.IMAGE_CAPTURE) // JPEG
+ val privUseCas2 = createUseCase(CaptureType.VIDEO_CAPTURE) // PRIV
+ val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+ put(privUseCase1, RESOLUTION_VGA)
+ put(jpegUseCase, RESOLUTION_VGA)
+ put(privUseCas2, RESOLUTION_VGA)
+ }
+ assertThrows(IllegalArgumentException::class.java) {
+ getSuggestedSpecsAndVerify(useCaseExpectedResultMap)
+ }
+ }
+
+ // //////////////////////////////////////////////////////////////////////////////////////////
+ //
+ // Resolution selection tests for LIMITED-level guaranteed configurations
+ //
+ // //////////////////////////////////////////////////////////////////////////////////////////
+
+ /**
+ * PRIV/PREVIEW + PRIV/RECORD
+ */
+ @Test
+ fun canSelectCorrectSizes_privPlusPriv_inLimitedDevice() {
+ val privUseCase1 = createUseCase(CaptureType.VIDEO_CAPTURE) // PRIV
+ val privUseCas2 = createUseCase(CaptureType.PREVIEW) // PRIV
+ val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+ put(privUseCase1, RECORD_SIZE)
+ put(privUseCas2, PREVIEW_SIZE)
+ }
+ getSuggestedSpecsAndVerify(
+ useCaseExpectedResultMap,
+ hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
+ )
+ }
+
+ /**
+ * PRIV/PREVIEW + YUV/RECORD
+ */
+ @Test
+ fun canSelectCorrectSizes_privPlusYuv_inLimitedDevice() {
+ val privUseCase = createUseCase(CaptureType.PREVIEW) // PRIV
+ val yuvUseCase = createUseCase(CaptureType.IMAGE_ANALYSIS) // YUV
+ val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+ put(privUseCase, PREVIEW_SIZE)
+ put(yuvUseCase, RECORD_SIZE)
+ }
+ getSuggestedSpecsAndVerify(
+ useCaseExpectedResultMap,
+ hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
+ )
+ }
+
+ /**
+ * YUV/PREVIEW + YUV/RECORD
+ */
+ @Test
+ fun canSelectCorrectSizes_yuvPlusYuv_inLimitedDevice() {
+ val yuvUseCase1 = createUseCase(CaptureType.IMAGE_ANALYSIS) // YUV
+ val yuvUseCase2 = createUseCase(CaptureType.IMAGE_ANALYSIS) // YUV
+ val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+ put(yuvUseCase1, RECORD_SIZE)
+ put(yuvUseCase2, PREVIEW_SIZE)
+ }
+ getSuggestedSpecsAndVerify(
+ useCaseExpectedResultMap,
+ hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
+ )
+ }
+
+ /**
+ * PRIV/PREVIEW + PRIV/RECORD + JPEG/RECORD
+ */
+ @Test
+ fun canSelectCorrectSizes_privPlusPrivPlusJpeg_inLimitedDevice() {
+ val privUseCase1 = createUseCase(CaptureType.VIDEO_CAPTURE) // PRIV
+ val privUseCase2 = createUseCase(CaptureType.PREVIEW) // PRIV
+ val jpegUseCase = createUseCase(CaptureType.IMAGE_CAPTURE) // JPEG
+ val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+ put(privUseCase1, RECORD_SIZE)
+ put(privUseCase2, PREVIEW_SIZE)
+ put(jpegUseCase, RECORD_SIZE)
+ }
+ getSuggestedSpecsAndVerify(
+ useCaseExpectedResultMap,
+ hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
+ )
+ }
+
+ /**
+ * PRIV/PREVIEW + YUV/RECORD + JPEG/RECORD
+ */
+ @Test
+ fun canSelectCorrectSizes_privPlusYuvPlusJpeg_inLimitedDevice() {
+ val privUseCase = createUseCase(CaptureType.PREVIEW) // PRIV
+ val yuvUseCase = createUseCase(CaptureType.IMAGE_ANALYSIS) // YUV
+ val jpegUseCase = createUseCase(CaptureType.IMAGE_CAPTURE) // JPEG
+ val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+ put(privUseCase, PREVIEW_SIZE)
+ put(yuvUseCase, RECORD_SIZE)
+ put(jpegUseCase, RECORD_SIZE)
+ }
+ getSuggestedSpecsAndVerify(
+ useCaseExpectedResultMap,
+ hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
+ )
+ }
+
+ /**
+ * YUV/PREVIEW + YUV/PREVIEW + JPEG/MAXIMUM
+ */
+ @Test
+ fun canSelectCorrectSizes_yuvPlusYuvPlusJpeg_inLimitedDevice() {
+ val yuvUseCase1 = createUseCase(CaptureType.IMAGE_ANALYSIS) // YUV
+ val yuvUseCase2 = createUseCase(CaptureType.IMAGE_ANALYSIS) // YUV
+ val jpegUseCase = createUseCase(CaptureType.IMAGE_CAPTURE) // JPEG
+ val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+ put(yuvUseCase1, PREVIEW_SIZE)
+ put(yuvUseCase2, PREVIEW_SIZE)
+ put(jpegUseCase, MAXIMUM_SIZE)
+ }
+ getSuggestedSpecsAndVerify(
+ useCaseExpectedResultMap,
+ hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
+ )
+ }
+
+ /**
+ * Unsupported YUV + PRIV + YUV for limited level devices
+ */
+ @Test
+ fun throwsException_unsupportedConfiguration_inLimitedDevice() {
+ val yuvUseCase1 = createUseCase(CaptureType.IMAGE_ANALYSIS) // YUV
+ val privUseCase = createUseCase(CaptureType.PREVIEW) // PRIV
+ val yuvUseCase2 = createUseCase(CaptureType.IMAGE_ANALYSIS) // YUV
+ val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+ put(yuvUseCase1, RESOLUTION_VGA)
+ put(privUseCase, RESOLUTION_VGA)
+ put(yuvUseCase2, RESOLUTION_VGA)
+ }
+ assertThrows(IllegalArgumentException::class.java) {
+ getSuggestedSpecsAndVerify(
+ useCaseExpectedResultMap,
+ hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
+ )
+ }
+ }
+
+ // //////////////////////////////////////////////////////////////////////////////////////////
+ //
+ // Resolution selection tests for FULL-level guaranteed configurations
+ //
+ // //////////////////////////////////////////////////////////////////////////////////////////
+
+ /**
+ * PRIV/PREVIEW + PRIV/MAXIMUM
+ */
+ @Test
+ fun canSelectCorrectSizes_privPlusPriv_inFullDevice() {
+ val privUseCase1 = createUseCase(CaptureType.VIDEO_CAPTURE) // PRIV
+ val privUseCase2 = createUseCase(CaptureType.PREVIEW) // PRIV
+ val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+ put(privUseCase1, MAXIMUM_SIZE)
+ put(privUseCase2, PREVIEW_SIZE)
+ }
+ getSuggestedSpecsAndVerify(
+ useCaseExpectedResultMap,
+ hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL
+ )
+ }
+
+ /**
+ * PRIV/PREVIEW + YUV/MAXIMUM
+ */
+ @Test
+ fun canSelectCorrectSizes_privPlusYuv_inFullDevice() {
+ val privUseCase = createUseCase(CaptureType.PREVIEW) // PRIV
+ val yuvUseCase = createUseCase(CaptureType.IMAGE_ANALYSIS) // YUV
+ val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+ put(privUseCase, PREVIEW_SIZE)
+ put(yuvUseCase, MAXIMUM_SIZE)
+ }
+ getSuggestedSpecsAndVerify(
+ useCaseExpectedResultMap,
+ hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL
+ )
+ }
+
+ /**
+ * YUV/PREVIEW + YUV/MAXIMUM
+ */
+ @Test
+ fun canSelectCorrectSizes_yuvPlusYuv_inFullDevice() {
+ val yuvUseCase1 = createUseCase(CaptureType.IMAGE_ANALYSIS) // YUV
+ val yuvUseCase2 = createUseCase(CaptureType.IMAGE_ANALYSIS) // YUV
+ val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+ put(yuvUseCase1, MAXIMUM_SIZE)
+ put(yuvUseCase2, PREVIEW_SIZE)
+ }
+ getSuggestedSpecsAndVerify(
+ useCaseExpectedResultMap,
+ hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL
+ )
+ }
+
+ /**
+ * PRIV/PREVIEW + PRIV/PREVIEW + JPEG/MAXIMUM
+ */
+ @Test
+ fun canSelectCorrectSizes_privPlusPrivPlusJpeg_inFullDevice() {
+ val jpegUseCase = createUseCase(CaptureType.IMAGE_CAPTURE) // JPEG
+ val privUseCase1 = createUseCase(CaptureType.VIDEO_CAPTURE) // PRIV
+ val privUseCase2 = createUseCase(CaptureType.PREVIEW) // PRIV
+ val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+ put(jpegUseCase, MAXIMUM_SIZE)
+ put(privUseCase1, PREVIEW_SIZE)
+ put(privUseCase2, PREVIEW_SIZE)
+ }
+ getSuggestedSpecsAndVerify(
+ useCaseExpectedResultMap,
+ hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL
+ )
+ }
+
+ /**
+ * YUV/VGA + PRIV/PREVIEW + YUV/MAXIMUM
+ */
+ @Test
+ fun canSelectCorrectSizes_yuvPlusPrivPlusYuv_inFullDevice() {
+ val privUseCase = createUseCase(CaptureType.PREVIEW) // PRIV
+ val yuvUseCase1 = createUseCase(CaptureType.IMAGE_ANALYSIS) // YUV
+ val yuvUseCase2 = createUseCase(CaptureType.IMAGE_ANALYSIS) // YUV
+ val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+ put(privUseCase, PREVIEW_SIZE)
+ put(yuvUseCase1, MAXIMUM_SIZE)
+ put(yuvUseCase2, RESOLUTION_VGA)
+ }
+ getSuggestedSpecsAndVerify(
+ useCaseExpectedResultMap,
+ hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL
+ )
+ }
+
+ /**
+ * YUV/VGA + YUV/PREVIEW + YUV/MAXIMUM
+ */
+ @Test
+ fun canSelectCorrectSizes_yuvPlusYuvPlusYuv_inFullDevice() {
+ val yuvUseCase1 = createUseCase(CaptureType.IMAGE_ANALYSIS) // YUV
+ val yuvUseCase2 = createUseCase(CaptureType.IMAGE_ANALYSIS) // YUV
+ val yuvUseCase3 = createUseCase(CaptureType.IMAGE_ANALYSIS) // YUV
+ val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+ put(yuvUseCase1, MAXIMUM_SIZE)
+ put(yuvUseCase2, PREVIEW_SIZE)
+ put(yuvUseCase3, RESOLUTION_VGA)
+ }
+ getSuggestedSpecsAndVerify(
+ useCaseExpectedResultMap,
+ hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL
+ )
+ }
+
+ /**
+ * Unsupported PRIV + PRIV + YUV + RAW for full level devices
+ */
+ @Test
+ fun throwsException_unsupportedConfiguration_inFullDevice() {
+ val privUseCase1 = createUseCase(CaptureType.VIDEO_CAPTURE) // PRIV
+ val privUseCase2 = createUseCase(CaptureType.PREVIEW) // PRIV
+ val yuvUseCase = createUseCase(CaptureType.IMAGE_ANALYSIS) // YUV
+ val rawUseCase = createRawUseCase() // RAW
+ val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+ put(privUseCase1, RESOLUTION_VGA)
+ put(privUseCase2, RESOLUTION_VGA)
+ put(yuvUseCase, RESOLUTION_VGA)
+ put(rawUseCase, RESOLUTION_VGA)
+ }
+ assertThrows(IllegalArgumentException::class.java) {
+ getSuggestedSpecsAndVerify(
+ useCaseExpectedResultMap,
+ hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
+ )
+ }
+ }
+
+ // //////////////////////////////////////////////////////////////////////////////////////////
+ //
+ // Resolution selection tests for Level-3 guaranteed configurations
+ //
+ // //////////////////////////////////////////////////////////////////////////////////////////
+
+ /**
+ * PRIV/PREVIEW + PRIV/VGA + YUV/MAXIMUM + RAW/MAXIMUM
+ */
+ @Test
+ fun canSelectCorrectSizes_privPlusPrivPlusYuvPlusRaw_inLevel3Device() {
+ val privUseCase1 = createUseCase(CaptureType.VIDEO_CAPTURE) // PRIV
+ val privUseCase2 = createUseCase(CaptureType.PREVIEW) // PRIV
+ val yuvUseCase = createUseCase(CaptureType.IMAGE_ANALYSIS) // YUV
+ val rawUseCase = createRawUseCase() // RAW
+ val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+ put(privUseCase1, PREVIEW_SIZE)
+ put(privUseCase2, RESOLUTION_VGA)
+ put(yuvUseCase, MAXIMUM_SIZE)
+ put(rawUseCase, MAXIMUM_SIZE)
+ }
+ getSuggestedSpecsAndVerify(
+ useCaseExpectedResultMap,
+ hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_3
+ )
+ }
+
+ /**
+ * PRIV/PREVIEW + PRIV/VGA + JPEG/MAXIMUM + RAW/MAXIMUM
+ */
+ @Test
+ fun canSelectCorrectSizes_privPlusPrivPlusJpegPlusRaw_inLevel3Device() {
+ val privUseCase1 = createUseCase(CaptureType.VIDEO_CAPTURE) // PRIV
+ val privUseCase2 = createUseCase(CaptureType.PREVIEW) // PRIV
+ val jpegUseCase = createUseCase(CaptureType.IMAGE_CAPTURE) // JPEG
+ val rawUseCase = createRawUseCase() // RAW
+ val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+ put(privUseCase1, PREVIEW_SIZE)
+ put(privUseCase2, RESOLUTION_VGA)
+ put(jpegUseCase, MAXIMUM_SIZE)
+ put(rawUseCase, MAXIMUM_SIZE)
+ }
+ getSuggestedSpecsAndVerify(
+ useCaseExpectedResultMap,
+ hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_3
+ )
+ }
+
+ /**
+ * Unsupported PRIV + YUV + YUV + RAW for level-3 devices
+ */
+ @Test
+ fun throwsException_unsupportedConfiguration_inLevel3Device() {
+ val privUseCase = createUseCase(CaptureType.PREVIEW) // PRIV
+ val yuvUseCase1 = createUseCase(CaptureType.IMAGE_ANALYSIS) // YUV
+ val yuvUseCase2 = createUseCase(CaptureType.IMAGE_ANALYSIS) // YUV
+ val rawUseCase = createRawUseCase() // RAW
+ val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+ put(privUseCase, RESOLUTION_VGA)
+ put(yuvUseCase1, RESOLUTION_VGA)
+ put(yuvUseCase2, RESOLUTION_VGA)
+ put(rawUseCase, RESOLUTION_VGA)
+ }
+ assertThrows(IllegalArgumentException::class.java) {
+ getSuggestedSpecsAndVerify(
+ useCaseExpectedResultMap,
+ hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_3
+ )
+ }
+ }
+
+ // //////////////////////////////////////////////////////////////////////////////////////////
+ //
+ // Resolution selection tests for Burst-capability guaranteed configurations
+ //
+ // //////////////////////////////////////////////////////////////////////////////////////////
+
+ /**
+ * PRIV/PREVIEW + PRIV/MAXIMUM
+ */
+ @Test
+ fun canSelectCorrectSizes_privPlusPriv_inLimitedDevice_withBurstCapability() {
+ val privUseCase1 = createUseCase(CaptureType.VIDEO_CAPTURE) // PRIV
+ val privUseCase2 = createUseCase(CaptureType.PREVIEW) // PRIV
+ val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+ put(privUseCase1, MAXIMUM_SIZE)
+ put(privUseCase2, PREVIEW_SIZE)
+ }
+ getSuggestedSpecsAndVerify(
+ useCaseExpectedResultMap,
+ hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
+ capabilities = intArrayOf(
+ CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE
+ )
+ )
+ }
+
+ /**
+ * PRIV/PREVIEW + YUV/MAXIMUM
+ */
+ @Test
+ fun canSelectCorrectSizes_privPlusYuv_inLimitedDevice_withBurstCapability() {
+ val privUseCase = createUseCase(CaptureType.PREVIEW) // PRIV
+ val yuvUseCase = createUseCase(CaptureType.IMAGE_ANALYSIS) // YUV
+ val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+ put(privUseCase, PREVIEW_SIZE)
+ put(yuvUseCase, MAXIMUM_SIZE)
+ }
+ getSuggestedSpecsAndVerify(
+ useCaseExpectedResultMap,
+ hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
+ capabilities = intArrayOf(
+ CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE
+ )
+ )
+ }
+
+ /**
+ * YUV/PREVIEW + YUV/MAXIMUM
+ */
+ @Test
+ fun canSelectCorrectSizes_yuvPlusYuv_inLimitedDevice_withBurstCapability() {
+ val yuvUseCase1 = createUseCase(CaptureType.IMAGE_ANALYSIS) // YUV
+ val yuvUseCase2 = createUseCase(CaptureType.IMAGE_ANALYSIS) // YUV
+ val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+ put(yuvUseCase1, MAXIMUM_SIZE)
+ put(yuvUseCase2, PREVIEW_SIZE)
+ }
+ getSuggestedSpecsAndVerify(
+ useCaseExpectedResultMap,
+ hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
+ capabilities = intArrayOf(
+ CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE
+ )
+ )
+ }
+
+ // //////////////////////////////////////////////////////////////////////////////////////////
+ //
+ // Resolution selection tests for RAW-capability guaranteed configurations
+ //
+ // //////////////////////////////////////////////////////////////////////////////////////////
+
+ /**
+ * RAW/MAX
+ */
+ @Test
+ fun canSelectCorrectSizes_singleRawStream_inLimitedDevice_withRawCapability() {
+ val rawUseCase = createRawUseCase() // RAW
+ val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+ put(rawUseCase, MAXIMUM_SIZE)
+ }
+ getSuggestedSpecsAndVerify(
+ useCaseExpectedResultMap,
+ hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
+ capabilities = intArrayOf(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW)
+ )
+ }
+
+ /**
+ * PRIV/PREVIEW + RAW/MAXIMUM
+ */
+ @Test
+ fun canSelectCorrectSizes_privPlusRAW_inLimitedDevice_withRawCapability() {
+ val privUseCase = createUseCase(CaptureType.PREVIEW) // PRIV
+ val rawUseCase = createRawUseCase() // RAW
+ val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+ put(privUseCase, PREVIEW_SIZE)
+ put(rawUseCase, MAXIMUM_SIZE)
+ }
+ getSuggestedSpecsAndVerify(
+ useCaseExpectedResultMap,
+ hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
+ capabilities = intArrayOf(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW)
+ )
+ }
+
+ /**
+ * PRIV/PREVIEW + PRIV/PREVIEW + RAW/MAXIMUM
+ */
+ @Test
+ fun canSelectCorrectSizes_privPlusPrivPlusRAW_inLimitedDevice_withRawCapability() {
+ val privUseCase1 = createUseCase(CaptureType.VIDEO_CAPTURE) // PRIV
+ val privUseCase2 = createUseCase(CaptureType.PREVIEW) // PRIV
+ val rawUseCase = createRawUseCase() // RAW
+ val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+ put(privUseCase1, PREVIEW_SIZE)
+ put(privUseCase2, PREVIEW_SIZE)
+ put(rawUseCase, MAXIMUM_SIZE)
+ }
+ getSuggestedSpecsAndVerify(
+ useCaseExpectedResultMap,
+ hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
+ capabilities = intArrayOf(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW)
+ )
+ }
+
+ /**
+ * PRIV/PREVIEW + YUV/PREVIEW + RAW/MAXIMUM
+ */
+ @Test
+ fun canSelectCorrectSizes_privPlusYuvPlusRAW_inLimitedDevice_withRawCapability() {
+ val privUseCase = createUseCase(CaptureType.PREVIEW) // PRIV
+ val yuvUseCase = createUseCase(CaptureType.IMAGE_ANALYSIS) // YUV
+ val rawUseCase = createRawUseCase() // RAW
+ val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+ put(privUseCase, PREVIEW_SIZE)
+ put(yuvUseCase, PREVIEW_SIZE)
+ put(rawUseCase, MAXIMUM_SIZE)
+ }
+ getSuggestedSpecsAndVerify(
+ useCaseExpectedResultMap,
+ hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
+ capabilities = intArrayOf(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW)
+ )
+ }
+
+ /**
+ * YUV/PREVIEW + YUV/PREVIEW + RAW/MAXIMUM
+ */
+ @Test
+ fun canSelectCorrectSizes_yuvPlusYuvPlusRAW_inLimitedDevice_withRawCapability() {
+ val yuvUseCase1 = createUseCase(CaptureType.IMAGE_ANALYSIS) // YUV
+ val yuvUseCase2 = createUseCase(CaptureType.IMAGE_ANALYSIS) // YUV
+ val rawUseCase = createRawUseCase() // RAW
+ val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+ put(yuvUseCase1, PREVIEW_SIZE)
+ put(yuvUseCase2, PREVIEW_SIZE)
+ put(rawUseCase, MAXIMUM_SIZE)
+ }
+ getSuggestedSpecsAndVerify(
+ useCaseExpectedResultMap,
+ hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
+ capabilities = intArrayOf(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW)
+ )
+ }
+
+ /**
+ * PRIV/PREVIEW + JPEG/MAXIMUM + RAW/MAXIMUM
+ */
+ @Test
+ fun canSelectCorrectSizes_privPlusJpegPlusRAW_inLimitedDevice_withRawCapability() {
+ val privUseCase = createUseCase(CaptureType.PREVIEW) // PRIV
+ val jpegUseCase = createUseCase(CaptureType.IMAGE_CAPTURE) // JPEG
+ val rawUseCase = createRawUseCase() // RAW
+ val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+ put(privUseCase, PREVIEW_SIZE)
+ put(jpegUseCase, MAXIMUM_SIZE)
+ put(rawUseCase, MAXIMUM_SIZE)
+ }
+ getSuggestedSpecsAndVerify(
+ useCaseExpectedResultMap,
+ hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
+ capabilities = intArrayOf(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW)
+ )
+ }
+
+ /**
+ * YUV/PREVIEW + JPEG/MAXIMUM + RAW/MAXIMUM
+ */
+ @Test
+ fun canSelectCorrectSizes_yuvPlusJpegPlusRAW_inLimitedDevice_withRawCapability() {
+ val yuvUseCase = createUseCase(CaptureType.IMAGE_ANALYSIS) // YUV
+ val jpegUseCase = createUseCase(CaptureType.IMAGE_CAPTURE) // JPEG
+ val rawUseCase = createRawUseCase() // RAW
+ val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+ put(yuvUseCase, PREVIEW_SIZE)
+ put(jpegUseCase, MAXIMUM_SIZE)
+ put(rawUseCase, MAXIMUM_SIZE)
+ }
+ getSuggestedSpecsAndVerify(
+ useCaseExpectedResultMap,
+ hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
+ capabilities = intArrayOf(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW)
+ )
+ }
+
+ private fun getSuggestedSpecsAndVerify(
+ useCasesExpectedResultMap: Map<UseCase, Size>,
+ attachedSurfaceInfoList: List<AttachedSurfaceInfo> = emptyList(),
+ hardwareLevel: Int = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY,
+ capabilities: IntArray? = null,
+ compareWithAtMost: Boolean = false
+ ) {
+ setupCameraAndInitCameraX(
+ hardwareLevel = hardwareLevel,
+ capabilities = capabilities
+ )
+ val supportedSurfaceCombination = SupportedSurfaceCombination(
+ context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
+ )
+
+ val useCaseConfigMap = getUseCaseToConfigMap(useCasesExpectedResultMap.keys.toList())
+ val useCaseConfigToOutputSizesMap =
+ getUseCaseConfigToOutputSizesMap(useCaseConfigMap.values.toList())
+ val suggestedStreamSpecs = supportedSurfaceCombination.getSuggestedStreamSpecifications(
+ false,
+ attachedSurfaceInfoList,
+ useCaseConfigToOutputSizesMap
+ )
+
+ useCasesExpectedResultMap.keys.forEach {
+ val resultSize = suggestedStreamSpecs[useCaseConfigMap[it]]!!.resolution
+ val expectedSize = useCasesExpectedResultMap[it]!!
+ if (!compareWithAtMost) {
+ assertThat(resultSize).isEqualTo(expectedSize)
+ } else {
+ assertThat(sizeIsAtMost(resultSize, expectedSize)).isTrue()
+ }
+ }
+ }
+
+ private fun getUseCaseToConfigMap(useCases: List<UseCase>): Map<UseCase, UseCaseConfig<*>> {
+ val useCaseConfigMap = mutableMapOf<UseCase, UseCaseConfig<*>>().apply {
+ useCases.forEach {
+ put(it, it.currentConfig)
+ }
+ }
+ return useCaseConfigMap
+ }
+
+ private fun getUseCaseConfigToOutputSizesMap(
+ useCaseConfigs: List<UseCaseConfig<*>>
+ ): Map<UseCaseConfig<*>, List<Size>> {
+ val resultMap = mutableMapOf<UseCaseConfig<*>, List<Size>>().apply {
+ useCaseConfigs.forEach {
+ put(it, DEFAULT_SUPPORTED_SIZES.toList())
+ }
+ }
+
+ return resultMap
+ }
+
+ /**
+ * Helper function that returns whether size is <= maxSize
+ *
+ */
+ private fun sizeIsAtMost(size: Size, maxSize: Size): Boolean {
+ return (size.height * size.width) <= (maxSize.height * maxSize.width)
+ }
+
+ // //////////////////////////////////////////////////////////////////////////////////////////
+ //
+ // Resolution selection tests for FPS settings
+ //
+ // //////////////////////////////////////////////////////////////////////////////////////////
+
+ @Test
+ fun getSupportedOutputSizes_single_valid_targetFPS() {
+ // a valid target means the device is capable of that fps
+ val useCase = createUseCase(CaptureType.PREVIEW, targetFrameRate = Range<Int>(25, 30))
+ val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+ put(useCase, Size(3840, 2160))
+ }
+ getSuggestedSpecsAndVerify(
+ useCaseExpectedResultMap,
+ hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
+ )
+ }
+
+ @Test
+ fun getSupportedOutputSizes_single_invalid_targetFPS() {
+ // an invalid target means the device would neve be able to reach that fps
+ val useCase = createUseCase(CaptureType.PREVIEW, targetFrameRate = Range<Int>(65, 70))
+ val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+ put(useCase, Size(800, 450))
+ }
+ getSuggestedSpecsAndVerify(
+ useCaseExpectedResultMap,
+ hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
+ )
+ }
+
+ @Test
+ fun getSupportedOutputSizes_multiple_targetFPS_first_is_larger() {
+ // a valid target means the device is capable of that fps
+ val useCase1 = createUseCase(CaptureType.PREVIEW, targetFrameRate = Range<Int>(30, 35))
+ val useCase2 = createUseCase(CaptureType.PREVIEW, targetFrameRate = Range<Int>(15, 25))
+ val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+ // both selected size should be no larger than 1920 x 1445
+ put(useCase1, Size(1920, 1445))
+ put(useCase2, Size(1920, 1445))
+ }
+ getSuggestedSpecsAndVerify(
+ useCaseExpectedResultMap,
+ hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL,
+ compareWithAtMost = true
+ )
+ }
+
+ @Test
+ fun getSupportedOutputSizes_multiple_targetFPS_first_is_smaller() {
+ // a valid target means the device is capable of that fps
+ val useCase1 = createUseCase(CaptureType.PREVIEW, targetFrameRate = Range<Int>(30, 35))
+ val useCase2 = createUseCase(CaptureType.PREVIEW, targetFrameRate = Range<Int>(45, 50))
+ val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+ // both selected size should be no larger than 1920 x 1440
+ put(useCase1, Size(1920, 1440))
+ put(useCase2, Size(1920, 1440))
+ }
+ getSuggestedSpecsAndVerify(
+ useCaseExpectedResultMap,
+ hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
+ compareWithAtMost = true
+ )
+ }
+
+ @Test
+ fun getSupportedOutputSizes_multiple_targetFPS_intersect() {
+ // first and second new use cases have target fps that intersect each other
+ val useCase1 = createUseCase(CaptureType.PREVIEW, targetFrameRate = Range<Int>(30, 40))
+ val useCase2 = createUseCase(CaptureType.PREVIEW, targetFrameRate = Range<Int>(35, 45))
+ val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+ // effective target fps becomes 35-40
+ // both selected size should be no larger than 1920 x 1080
+ put(useCase1, Size(1920, 1080))
+ put(useCase2, Size(1920, 1080))
+ }
+ getSuggestedSpecsAndVerify(
+ useCaseExpectedResultMap,
+ hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
+ compareWithAtMost = true
+ )
+ }
+
+ @Test
+ fun getSupportedOutputSizes_multiple_cases_first_has_targetFPS() {
+ // first new use case has a target fps, second new use case does not
+ val useCase1 = createUseCase(CaptureType.PREVIEW, targetFrameRate = Range<Int>(30, 35))
+ val useCase2 = createUseCase(CaptureType.PREVIEW)
+ val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+ // both selected size should be no larger than 1920 x 1440
+ put(useCase1, Size(1920, 1440))
+ put(useCase2, Size(1920, 1440))
+ }
+ getSuggestedSpecsAndVerify(
+ useCaseExpectedResultMap,
+ hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
+ compareWithAtMost = true
+ )
+ }
+
+ @Test
+ fun getSupportedOutputSizes_multiple_cases_second_has_targetFPS() {
+ // second new use case does not have a target fps, first new use case does not
+ val useCase1 = createUseCase(CaptureType.PREVIEW)
+ val useCase2 = createUseCase(CaptureType.PREVIEW, targetFrameRate = Range<Int>(30, 35))
+ val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+ // both selected size should be no larger than 1920 x 1440
+ put(useCase1, Size(1920, 1440))
+ put(useCase2, Size(1920, 1440))
+ }
+ getSuggestedSpecsAndVerify(
+ useCaseExpectedResultMap,
+ hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
+ compareWithAtMost = true
+ )
+ }
+
+ @Test
+ fun getSupportedOutputSizes_attached_with_targetFPS_no_new_targetFPS() {
+ // existing surface with target fps + new use case without a target fps
+ val useCase = createUseCase(CaptureType.PREVIEW)
+ val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+ // size should be no larger than 1280 x 960
+ put(useCase, Size(1280, 960))
+ }
+ // existing surface w/ target fps
+ val attachedSurfaceInfo = AttachedSurfaceInfo.create(
+ SurfaceConfig.create(
+ ConfigType.JPEG,
+ ConfigSize.PREVIEW
+ ), ImageFormat.JPEG,
+ Size(1280, 720), Range(40, 50)
+ )
+ getSuggestedSpecsAndVerify(
+ useCaseExpectedResultMap,
+ attachedSurfaceInfoList = listOf(attachedSurfaceInfo),
+ hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
+ compareWithAtMost = true
+ )
+ }
+
+ @Test
+ fun getSupportedOutputSizes_attached_with_targetFPS_and_new_targetFPS_no_intersect() {
+ // existing surface with target fps + new use case with target fps that does not intersect
+ val useCase = createUseCase(CaptureType.PREVIEW, targetFrameRate = Range<Int>(30, 35))
+ val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+ // size of new surface should be no larger than 1280 x 960
+ put(useCase, Size(1280, 960))
+ }
+ // existing surface w/ target fps
+ val attachedSurfaceInfo = AttachedSurfaceInfo.create(
+ SurfaceConfig.create(
+ ConfigType.JPEG,
+ ConfigSize.PREVIEW
+ ), ImageFormat.JPEG,
+ Size(1280, 720), Range(40, 50)
+ )
+ getSuggestedSpecsAndVerify(
+ useCaseExpectedResultMap,
+ attachedSurfaceInfoList = listOf(attachedSurfaceInfo),
+ hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
+ compareWithAtMost = true
+ )
+ }
+
+ @Test
+ fun getSupportedOutputSizes_attached_with_targetFPS_and_new_targetFPS_with_intersect() {
+ // existing surface with target fps + new use case with target fps that intersect each other
+ val useCase = createUseCase(CaptureType.PREVIEW, targetFrameRate = Range<Int>(45, 50))
+ val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+ // size of new surface should be no larger than 1280 x 720
+ put(useCase, Size(1280, 720))
+ }
+ // existing surface w/ target fps
+ val attachedSurfaceInfo = AttachedSurfaceInfo.create(
+ SurfaceConfig.create(
+ ConfigType.JPEG,
+ ConfigSize.PREVIEW
+ ), ImageFormat.JPEG,
+ Size(1280, 720), Range(40, 50)
+ )
+ getSuggestedSpecsAndVerify(
+ useCaseExpectedResultMap,
+ attachedSurfaceInfoList = listOf(attachedSurfaceInfo),
+ hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
+ compareWithAtMost = true
+ )
+ }
+
+ // //////////////////////////////////////////////////////////////////////////////////////////
+ //
+ // Other tests
+ //
+ // //////////////////////////////////////////////////////////////////////////////////////////
+
@Test
fun getMaximumSizeForImageFormat() {
setupCameraAndInitCameraX()
@@ -1498,1043 +1599,6 @@
}
@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 suggestedStreamSpecMap = getSuggestedStreamSpecMap(
- false, supportedSurfaceCombination, useCase)
- assertThat(suggestedStreamSpecMap[useCase]?.resolution).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)
- // Non-matched items have been removed by OutputSizesCorrector due to
- // TargetAspectRatio quirk.
- )
- } 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)
- // Non-matched items have been removed by OutputSizesCorrector due to
- // TargetAspectRatio quirk.
- )
- } 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 getSupportedOutputSizes_setCustomOrderedResolutions() {
- setupCameraAndInitCameraX(
- hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
- )
- val supportedSurfaceCombination = SupportedSurfaceCombination(
- context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
- )
- val customOrderedResolutions = listOf(
- Size(640, 480),
- Size(1280, 720),
- Size(1920, 1080),
- Size(3840, 2160),
- )
- val useCase = createUseCaseByLegacyApi(
- FAKE_USE_CASE,
- customOrderedResolutions = customOrderedResolutions,
- maxResolution = Size(1920, 1440),
- defaultResolution = Size(1280, 720),
- supportedResolutions = listOf(
- Pair.create(
- ImageFormat.PRIVATE, arrayOf(
- Size(800, 450),
- Size(640, 480),
- Size(320, 240),
- )
- )
- )
- )
- // Custom ordered resolutions is fully respected, meaning it will not be sorted or filtered
- // by other configurations such as max/default/target/supported resolutions.
- val resultList = getSupportedOutputSizes(supportedSurfaceCombination, useCase)
- assertThat(resultList).containsExactlyElementsIn(customOrderedResolutions).inOrder()
- }
-
- @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 suggestedStreamSpecMap = getSuggestedStreamSpecMap(
- false, supportedSurfaceCombination, useCase)
- // Checks mMaximumSize is final selected for the use case.
- assertThat(suggestedStreamSpecMap[useCase]?.resolution).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(
- hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
- 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)
@@ -2549,652 +1613,6 @@
)
}
- @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 suggestedStreamSpecMap = getSuggestedStreamSpecMap(
- false, supportedSurfaceCombination, useCase)
- // Checks 640x480 is final selected for the use case.
- assertThat(suggestedStreamSpecMap[useCase]?.resolution).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 suggestedStreamSpecMap = getSuggestedStreamSpecMap(
- false, supportedSurfaceCombination, useCase)
- // Checks 480x480 is final selected for the use case.
- assertThat(suggestedStreamSpecMap[useCase]?.resolution).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 suggestedStreamSpecMap = getSuggestedStreamSpecMap(
- false,
- supportedSurfaceCombination,
- preview,
- imageCapture,
- imageAnalysis
- )
- assertThat(suggestedStreamSpecMap[imageAnalysis]?.resolution).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 suggestedStreamSpecMap = getSuggestedStreamSpecMap(
- false,
- supportedSurfaceCombination,
- preview,
- imageCapture,
- imageAnalysis
- )
- assertThat(suggestedStreamSpecMap[imageAnalysis]?.resolution).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 suggestedStreamSpecMap = getSuggestedStreamSpecMap(
- false, supportedSurfaceCombination, useCase)
-
- // Checks 8000x6000 is final selected for the use case.
- assertThat(suggestedStreamSpecMap[useCase]?.resolution).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 suggestedStreamSpecMap = getSuggestedStreamSpecMap(
- false, supportedSurfaceCombination, useCase)
-
- // Checks 8000x6000 is final selected for the use case.
- assertThat(suggestedStreamSpecMap[useCase]?.resolution).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 suggestedStreamSpecMap = getSuggestedStreamSpecMap(
- false, supportedSurfaceCombination, useCase)
-
- // Checks 8000x6000 is final selected for the use case.
- assertThat(suggestedStreamSpecMap[useCase]?.resolution).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 suggestedStreamSpecMap = getSuggestedStreamSpecMap(
- false, supportedSurfaceCombination, useCase)
-
- // Checks 8000x6000 is final selected for the use case.
- assertThat(suggestedStreamSpecMap[useCase]?.resolution).isEqualTo(Size(8000, 4500))
- }
-
- @Test
- @Throws(CameraUnavailableException::class, CameraAccessExceptionCompat::class)
- fun getSupportedOutputSizes_single_valid_targetFPS() {
- // a valid target means the device is capable of that fps
- setupCameraAndInitCameraX(
- hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
- )
- val supportedSurfaceCombination = SupportedSurfaceCombination(
- context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
- )
-
- // use case with target fps
- val useCase1 = createUseCaseByLegacyApi(
- FAKE_USE_CASE,
- targetFrameRate = Range<Int>(25, 30)
- )
-
- val suggestedStreamSpecMap = getSuggestedStreamSpecMap(
- false,
- supportedSurfaceCombination,
- useCase1
- )
- // single selected size should be equal to 3840 x 2160
- assertThat(suggestedStreamSpecMap[useCase1]!!.resolution).isEqualTo(Size(3840, 2160))
- }
-
- @Test
- @Throws(CameraUnavailableException::class, CameraAccessExceptionCompat::class)
- fun getSupportedOutputSizes_single_invalid_targetFPS() {
- // an invalid target means the device would neve be able to reach that fps
- setupCameraAndInitCameraX(
- hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
- )
- val supportedSurfaceCombination = SupportedSurfaceCombination(
- context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
- )
-
- // use case with target fps
- val useCase1 = createUseCaseByLegacyApi(
- FAKE_USE_CASE,
- targetFrameRate = Range<Int>(65, 70)
- )
-
- val suggestedStreamSpecMap = getSuggestedStreamSpecMap(
- false,
- supportedSurfaceCombination,
- useCase1
- )
- // single selected size should be equal to 3840 x 2160
- assertThat(suggestedStreamSpecMap[useCase1]!!.resolution).isEqualTo(Size(800, 450))
- }
-
- @Test
- @Throws(CameraUnavailableException::class, CameraAccessExceptionCompat::class)
- fun getSupportedOutputSizes_multiple_targetFPS_first_is_larger() {
- // a valid target means the device is capable of that fps
- setupCameraAndInitCameraX(
- hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL
- )
- val supportedSurfaceCombination = SupportedSurfaceCombination(
- context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
- )
-
- val useCase1 = createUseCaseByLegacyApi(
- FAKE_USE_CASE,
- targetFrameRate = Range<Int>(30, 35),
- surfaceOccupancyPriority = 1
- )
-
- val useCase2 = createUseCaseByLegacyApi(
- FAKE_USE_CASE,
- targetFrameRate = Range<Int>(15, 25)
- )
-
- val suggestedStreamSpecMap = getSuggestedStreamSpecMap(
- false,
- supportedSurfaceCombination,
- useCase1,
- useCase2
- )
- // both selected size should be no larger than 1920 x 1080
- assertThat(sizeIsAtMost(suggestedStreamSpecMap[useCase1]!!.resolution, Size(1920, 1445)))
- .isTrue()
- assertThat(sizeIsAtMost(suggestedStreamSpecMap[useCase2]!!.resolution, Size(1920, 1445)))
- .isTrue()
- }
-
- @Test
- @Throws(CameraUnavailableException::class, CameraAccessExceptionCompat::class)
- fun getSupportedOutputSizes_multiple_targetFPS_first_is_smaller() {
- // a valid target means the device is capable of that fps
- setupCameraAndInitCameraX(
- hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
- )
- val supportedSurfaceCombination = SupportedSurfaceCombination(
- context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
- )
-
- val useCase1 = createUseCaseByLegacyApi(
- FAKE_USE_CASE,
- targetFrameRate = Range<Int>(30, 35),
- surfaceOccupancyPriority = 1
- )
-
- val useCase2 = createUseCaseByLegacyApi(
- FAKE_USE_CASE,
- targetFrameRate = Range<Int>(45, 50)
- )
-
- val suggestedStreamSpecMap = getSuggestedStreamSpecMap(
- false,
- supportedSurfaceCombination,
- useCase1,
- useCase2
- )
- // both selected size should be no larger than 1920 x 1440
- assertThat(sizeIsAtMost(suggestedStreamSpecMap[useCase1]!!.resolution, Size(1920, 1440)))
- .isTrue()
- assertThat(sizeIsAtMost(suggestedStreamSpecMap[useCase2]!!.resolution, Size(1920, 1440)))
- .isTrue()
- }
-
- @Test
- @Throws(CameraUnavailableException::class, CameraAccessExceptionCompat::class)
- fun getSupportedOutputSizes_multiple_targetFPS_intersect() {
- // first and second new use cases have target fps that intersect each other
- setupCameraAndInitCameraX(
- hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
- )
- val supportedSurfaceCombination = SupportedSurfaceCombination(
- context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
- )
-
- val useCase1 = createUseCaseByLegacyApi(
- FAKE_USE_CASE,
- targetFrameRate = Range<Int>(30, 40),
- surfaceOccupancyPriority = 1
- )
-
- val useCase2 = createUseCaseByLegacyApi(
- FAKE_USE_CASE,
- targetFrameRate = Range<Int>(35, 45)
- )
-
- val suggestedStreamSpecMap = getSuggestedStreamSpecMap(
- false,
- supportedSurfaceCombination,
- useCase1,
- useCase2
- )
- // effective target fps becomes 35-40
- // both selected size should be no larger than 1920 x 1080
- assertThat(sizeIsAtMost(suggestedStreamSpecMap[useCase1]!!.resolution, Size(1920, 1080)))
- .isTrue()
- assertThat(sizeIsAtMost(suggestedStreamSpecMap[useCase2]!!.resolution, Size(1920, 1080)))
- .isTrue()
- }
- @Test
- @Throws(CameraUnavailableException::class, CameraAccessExceptionCompat::class)
- fun getSupportedOutputSizes_multiple_cases_first_has_targetFPS() {
- // first new use case has a target fps, second new use case does not
- setupCameraAndInitCameraX(
- hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
- )
- val supportedSurfaceCombination = SupportedSurfaceCombination(
- context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
- )
-
- val useCase1 = createUseCaseByLegacyApi(
- FAKE_USE_CASE,
- targetFrameRate = Range<Int>(30, 35),
- surfaceOccupancyPriority = 1
- )
-
- val useCase2 = createUseCaseByLegacyApi(
- FAKE_USE_CASE
- )
-
- val suggestedStreamSpecMap = getSuggestedStreamSpecMap(
- false,
- supportedSurfaceCombination,
- useCase1,
- useCase2
- )
- // both selected size should be no larger than 1920 x 1440
- assertThat(sizeIsAtMost(suggestedStreamSpecMap[useCase1]!!.resolution, Size(1920, 1440)))
- .isTrue()
- assertThat(sizeIsAtMost(suggestedStreamSpecMap[useCase2]!!.resolution, Size(1920, 1440)))
- .isTrue()
- }
-
- @Test
- @Throws(CameraUnavailableException::class, CameraAccessExceptionCompat::class)
- fun getSupportedOutputSizes_multiple_cases_second_has_targetFPS() {
- // second new use case does not have a target fps, first new use case does not
- setupCameraAndInitCameraX(
- hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
- )
- val supportedSurfaceCombination = SupportedSurfaceCombination(
- context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
- )
-
- val useCase1 = createUseCaseByLegacyApi(
- FAKE_USE_CASE
- )
- val useCase2 = createUseCaseByLegacyApi(
- FAKE_USE_CASE,
- targetFrameRate = Range<Int>(30, 35)
- )
-
- val suggestedStreamSpecMap = getSuggestedStreamSpecMap(
- false,
- supportedSurfaceCombination,
- useCase1,
- useCase2
- )
- // both selected size should be no larger than 1920 x 1440
- assertThat(sizeIsAtMost(suggestedStreamSpecMap[useCase1]!!.resolution, Size(1920, 1440)))
- .isTrue()
- assertThat(sizeIsAtMost(suggestedStreamSpecMap[useCase2]!!.resolution, Size(1920, 1440)))
- .isTrue()
- }
-
- @Test
- @Throws(CameraUnavailableException::class, CameraAccessExceptionCompat::class)
- fun getSupportedOutputSizes_attached_with_targetFPS_no_new_targetFPS() {
- // existing surface with target fps + new use case without a target fps
- setupCameraAndInitCameraX(
- hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
- )
- val supportedSurfaceCombination = SupportedSurfaceCombination(
- context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
- )
-
- // existing surface w/ target fps
- val attachedSurfaceInfo = AttachedSurfaceInfo.create(
- SurfaceConfig.create(
- ConfigType.JPEG,
- ConfigSize.PREVIEW
- ), ImageFormat.JPEG,
- Size(1280, 720), Range(40, 50)
- )
-
- // new use case with no target fps
- val useCase1 = createUseCaseByLegacyApi(FAKE_USE_CASE)
-
- val suggestedStreamSpecMap = getSuggestedStreamSpecMap(
- false,
- supportedSurfaceCombination,
- useCase1,
- attachedSurfaces = listOf(attachedSurfaceInfo)
- )
- // size should be no larger than 1280 x 960
- assertThat(sizeIsAtMost(suggestedStreamSpecMap[useCase1]!!.resolution, Size(1280, 960)))
- .isTrue()
- }
- @Test
- @Throws(CameraUnavailableException::class, CameraAccessExceptionCompat::class)
- fun getSupportedOutputSizes_attached_with_targetFPS_and_new_targetFPS_no_intersect() {
- // existing surface with target fps + new use case with target fps that does not intersect
- setupCameraAndInitCameraX(
- hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
- )
- val supportedSurfaceCombination = SupportedSurfaceCombination(
- context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
- )
-
- // existing surface w/ target fps
- val attachedSurfaceInfo = AttachedSurfaceInfo.create(
- SurfaceConfig.create(
- ConfigType.JPEG,
- ConfigSize.PREVIEW
- ), ImageFormat.JPEG,
- Size(1280, 720), Range(40, 50)
- )
-
- // new use case with target fps
- val useCase1 = createUseCaseByLegacyApi(
- FAKE_USE_CASE,
- targetFrameRate = Range<Int>(30, 35)
-
- )
-
- val suggestedStreamSpecMap = getSuggestedStreamSpecMap(
- false,
- supportedSurfaceCombination,
- useCase1,
- attachedSurfaces = listOf(attachedSurfaceInfo)
- )
- // size of new surface should be no larger than 1280 x 960
- assertThat(sizeIsAtMost(suggestedStreamSpecMap[useCase1]!!.resolution, Size(1280, 960)))
- .isTrue()
- }
-
- @Test
- @Throws(CameraUnavailableException::class, CameraAccessExceptionCompat::class)
- fun getSupportedOutputSizes_attached_with_targetFPS_and_new_targetFPS_with_intersect() {
- // existing surface with target fps + new use case with target fps that intersect each other
- setupCameraAndInitCameraX(
- hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
- )
- val supportedSurfaceCombination = SupportedSurfaceCombination(
- context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
- )
-
- // existing surface w/ target fps
- val attachedSurfaceInfo = AttachedSurfaceInfo.create(
- SurfaceConfig.create(
- ConfigType.JPEG,
- ConfigSize.PREVIEW
- ), ImageFormat.JPEG,
- Size(1280, 720), Range(40, 50)
- )
-
- // new use case with target fps
- val useCase1 = createUseCaseByLegacyApi(
- FAKE_USE_CASE,
- targetFrameRate = Range<Int>(45, 50)
-
- )
-
- val suggestedStreamSpecMap = getSuggestedStreamSpecMap(
- false,
- supportedSurfaceCombination,
- useCase1,
- attachedSurfaces = listOf(attachedSurfaceInfo)
- )
- // size of new surface should be no larger than 1280 x 720
- assertThat(sizeIsAtMost(suggestedStreamSpecMap[useCase1]!!.resolution, Size(1280, 720)))
- .isTrue()
- }
-
- /**
- * Helper function that returns whether size is <= maxSize
- *
- */
- private fun sizeIsAtMost(size: Size, maxSize: Size): Boolean {
- return (size.height * size.width) <= (maxSize.height * maxSize.width)
- }
-
/**
* Sets up camera according to the specified settings and initialize [CameraX].
*
@@ -3255,17 +1673,134 @@
}
/**
+ * Sets up camera according to the specified settings.
+ *
+ * @param cameraId the camera id to be set up. Default value is [DEFAULT_CAMERA_ID].
+ * @param hardwareLevel the hardware level of the camera. Default value is
+ * [CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY].
+ * @param sensorOrientation the sensor orientation of the camera. Default value is
+ * [SENSOR_ORIENTATION_90].
+ * @param pixelArraySize the active pixel array size of the camera. Default value is
+ * [LANDSCAPE_PIXEL_ARRAY_SIZE].
+ * @param supportedSizes the supported sizes of the camera. Default value is
+ * [DEFAULT_SUPPORTED_SIZES].
+ * @param capabilities the capabilities of the camera. Default value is null.
+ */
+ fun setupCamera(
+ cameraId: String = DEFAULT_CAMERA_ID,
+ hardwareLevel: Int = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY,
+ sensorOrientation: Int = SENSOR_ORIENTATION_90,
+ pixelArraySize: Size = LANDSCAPE_PIXEL_ARRAY_SIZE,
+ supportedSizes: Array<Size> = DEFAULT_SUPPORTED_SIZES,
+ supportedHighResolutionSizes: Array<Size>? = null,
+ capabilities: IntArray? = null
+ ) {
+ val mockMap = Mockito.mock(StreamConfigurationMap::class.java).also {
+ // Sets up the supported sizes
+ Mockito.`when`(it.getOutputSizes(ArgumentMatchers.anyInt()))
+ .thenReturn(supportedSizes)
+ // ImageFormat.PRIVATE was supported since API level 23. Before that, the supported
+ // output sizes need to be retrieved via SurfaceTexture.class.
+ Mockito.`when`(it.getOutputSizes(SurfaceTexture::class.java))
+ .thenReturn(supportedSizes)
+ // This is setup for the test to determine RECORD size from StreamConfigurationMap
+ Mockito.`when`(it.getOutputSizes(MediaRecorder::class.java))
+ .thenReturn(supportedSizes)
+
+ // setup to return different minimum frame durations depending on resolution
+ // minimum frame durations were designated only for the purpose of testing
+ Mockito.`when`(it.getOutputMinFrameDuration(
+ ArgumentMatchers.anyInt(),
+ ArgumentMatchers.eq(Size(4032, 3024))
+ ))
+ .thenReturn(50000000L) // 20 fps, size maximum
+
+ Mockito.`when`(it.getOutputMinFrameDuration(
+ ArgumentMatchers.anyInt(),
+ ArgumentMatchers.eq(Size(3840, 2160))
+ ))
+ .thenReturn(40000000L) // 25, size record
+
+ Mockito.`when`(it.getOutputMinFrameDuration(
+ ArgumentMatchers.anyInt(),
+ ArgumentMatchers.eq(Size(1920, 1440))
+ ))
+ .thenReturn(30000000L) // 30
+
+ Mockito.`when`(it.getOutputMinFrameDuration(
+ ArgumentMatchers.anyInt(),
+ ArgumentMatchers.eq(Size(1920, 1080))
+ ))
+ .thenReturn(28000000L) // 35
+
+ Mockito.`when`(it.getOutputMinFrameDuration(
+ ArgumentMatchers.anyInt(),
+ ArgumentMatchers.eq(Size(1280, 960))
+ ))
+ .thenReturn(25000000L) // 40
+
+ Mockito.`when`(it.getOutputMinFrameDuration(
+ ArgumentMatchers.anyInt(),
+ ArgumentMatchers.eq(Size(1280, 720))
+ ))
+ .thenReturn(22000000L) // 45, size preview/display
+
+ Mockito.`when`(it.getOutputMinFrameDuration(
+ ArgumentMatchers.anyInt(),
+ ArgumentMatchers.eq(Size(960, 544))
+ ))
+ .thenReturn(20000000L) // 50
+
+ Mockito.`when`(it.getOutputMinFrameDuration(
+ ArgumentMatchers.anyInt(),
+ ArgumentMatchers.eq(Size(800, 450))
+ ))
+ .thenReturn(16666000L) // 60fps
+
+ Mockito.`when`(it.getOutputMinFrameDuration(
+ ArgumentMatchers.anyInt(),
+ ArgumentMatchers.eq(Size(640, 480))
+ ))
+ .thenReturn(16666000L) // 60fps
+
+ // Sets up the supported high resolution sizes
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ Mockito.`when`(it.getHighResolutionOutputSizes(ArgumentMatchers.anyInt()))
+ .thenReturn(supportedHighResolutionSizes)
+ }
+ }
+
+ val characteristics = ShadowCameraCharacteristics.newCameraCharacteristics()
+ Shadow.extract<ShadowCameraCharacteristics>(characteristics).apply {
+ set(CameraCharacteristics.LENS_FACING, CameraCharacteristics.LENS_FACING_BACK)
+ set(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL, hardwareLevel)
+ set(CameraCharacteristics.SENSOR_ORIENTATION, sensorOrientation)
+ set(CameraCharacteristics.SENSOR_INFO_PIXEL_ARRAY_SIZE, pixelArraySize)
+ set(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP, mockMap)
+ capabilities?.let {
+ set(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES, it)
+ }
+ }
+
+ val cameraManager = ApplicationProvider.getApplicationContext<Context>()
+ .getSystemService(Context.CAMERA_SERVICE) as CameraManager
+ (Shadow.extract<Any>(cameraManager) as ShadowCameraManager)
+ .addCamera(cameraId, characteristics)
+ }
+
+ /**
* Initializes the [CameraX].
*/
private fun initCameraX() {
val surfaceManagerProvider =
CameraDeviceSurfaceManager.Provider { context, _, availableCameraIds ->
- Camera2DeviceSurfaceManager(
+ cameraDeviceSurfaceManager = Camera2DeviceSurfaceManager(
context,
mockCamcorderProfileHelper,
CameraManagerCompat.from(this@SupportedSurfaceCombinationTest.context),
availableCameraIds
)
+ cameraDeviceSurfaceManager
}
val cameraXConfig = CameraXConfig.Builder.fromConfig(Camera2Config.defaultConfig())
.setDeviceSurfaceManagerProvider(surfaceManagerProvider)
@@ -3298,7 +1833,9 @@
removeAt(index)
}
if (!supportedSurfaceCombination.checkSupported(
- isConcurrentCameraModeOn, subConfigurationList)) {
+ isConcurrentCameraModeOn, subConfigurationList
+ )
+ ) {
return false
}
}
@@ -3306,164 +1843,27 @@
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 getSuggestedStreamSpecMap(
- isConcurrentCameraModeOn: Boolean,
- supportedSurfaceCombination: SupportedSurfaceCombination,
- vararg useCases: UseCase,
- attachedSurfaces: List<AttachedSurfaceInfo>? = null,
- cameraFactory: CameraFactory = this.cameraFactory!!,
- cameraId: String = DEFAULT_CAMERA_ID,
- useCaseConfigFactory: UseCaseConfigFactory = this.useCaseConfigFactory!!
- ): Map<UseCase, StreamSpec?> {
- // 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 stream specs
- val useCaseConfigStreamSpecMap = supportedSurfaceCombination
- .getSuggestedStreamSpecifications(
- isConcurrentCameraModeOn,
- attachedSurfaces ?: emptyList(),
- mutableListOf<UseCaseConfig<*>?>().apply { addAll(useCaseToConfigMap.values) }
- )
- val useCaseStreamSpecMap = mutableMapOf<UseCase, StreamSpec?>()
- // Maps the use cases to the suggestion resolutions
- for (useCase in useCases) {
- useCaseStreamSpecMap[useCase] = useCaseConfigStreamSpecMap[useCaseToConfigMap[useCase]]
- }
- return useCaseStreamSpecMap
- }
-
- /**
- * 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] or FakeUseCase according to the specified
- * settings.
- *
- * @param useCaseType Which of [Preview], [ImageCapture], [ImageAnalysis] 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.
- * @param customOrderedResolutions the custom ordered resolutions. Default is null.
- */
- private fun createUseCaseByLegacyApi(
- useCaseType: Int,
- targetRotation: Int = UNKNOWN_ROTATION,
- targetAspectRatio: Int = UNKNOWN_ASPECT_RATIO,
- targetResolution: Size? = null,
- targetFrameRate: Range<Int>? = null,
- surfaceOccupancyPriority: Int = -1,
- maxResolution: Size? = null,
- defaultResolution: Size? = null,
- supportedResolutions: List<Pair<Int, Array<Size>>>? = null,
- customOrderedResolutions: List<Size>? = null,
+ private fun createUseCase(
+ captureType: CaptureType,
+ targetFrameRate: Range<Int>? = 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)
+ val builder = FakeUseCaseConfig.Builder(captureType, when (captureType) {
+ CaptureType.IMAGE_CAPTURE -> ImageFormat.JPEG
+ CaptureType.IMAGE_ANALYSIS -> ImageFormat.YUV_420_888
+ else -> INTERNAL_DEFINED_IMAGE_FORMAT_PRIVATE
+ })
+ targetFrameRate?.let {
+ builder.mutableConfig.insertOption(UseCaseConfig.OPTION_TARGET_FRAME_RATE, it)
}
- if (targetRotation != UNKNOWN_ROTATION) {
- builder.setTargetRotation(targetRotation)
- }
- if (targetAspectRatio != UNKNOWN_ASPECT_RATIO) {
- builder.setTargetAspectRatio(targetAspectRatio)
- }
- if (surfaceOccupancyPriority >= 0) {
- builder.setSurfaceOccupancyPriority(surfaceOccupancyPriority)
- }
- builder.mutableConfig.insertOption(OPTION_TARGET_FRAME_RATE, targetFrameRate)
- targetResolution?.let { builder.setTargetResolution(it) }
- maxResolution?.let { builder.setMaxResolution(it) }
- defaultResolution?.let { builder.setDefaultResolution(it) }
- supportedResolutions?.let { builder.setSupportedResolutions(it) }
- customOrderedResolutions?.let { builder.setCustomOrderedResolutions(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 one ore more specific Quality */
- private fun createVideoCapture(vararg quality: Quality): VideoCapture<TestVideoOutput> {
- return createVideoCapture(QualitySelector.fromOrderedList(listOf(*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,
- targetFrameRate: Range<Int>? = null,
- surfaceOccupancyPriority: Int = -1,
- maxResolution: Size? = null,
- highResolutionEnabled: Boolean = false,
- defaultResolution: Size? = null,
- supportedResolutions: List<Pair<Int, Array<Size>>>? = null,
- customOrderedResolutions: List<Size>? = null,
- ): UseCase
+ private fun createRawUseCase(): UseCase {
+ val builder = FakeUseCaseConfig.Builder()
+ builder.mutableConfig.insertOption(
+ UseCaseConfig.OPTION_INPUT_FORMAT,
+ ImageFormat.RAW_SENSOR
+ )
+ return builder.build()
}
}
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/impl/CameraDeviceSurfaceManager.java b/camera/camera-core/src/main/java/androidx/camera/core/impl/CameraDeviceSurfaceManager.java
index 66e9cf6..48bd32c 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/impl/CameraDeviceSurfaceManager.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/impl/CameraDeviceSurfaceManager.java
@@ -87,12 +87,16 @@
/**
* Retrieves a map of suggested stream specifications for the given list of use cases.
*
- * @param isConcurrentCameraModeOn true if concurrent camera mode is on, otherwise false.
- * @param cameraId the camera id of the camera device used by the use cases
- * @param existingSurfaces list of surfaces already configured and used by the camera. The
- * stream specifications for these surface can not change.
- * @param newUseCaseConfigs list of configurations of the use cases that will be given a
- * suggested stream specification
+ * @param isConcurrentCameraModeOn true if concurrent camera mode is on, otherwise
+ * false.
+ * @param cameraId the camera id of the camera device used by the
+ * use cases
+ * @param existingSurfaces list of surfaces already configured and used by
+ * the camera. The stream specifications for these
+ * surface can not change.
+ * @param newUseCaseConfigsSupportedSizeMap map of configurations of the use cases to the
+ * supported output sizes list that will be given a
+ * suggested stream specification
* @return map of suggested stream specifications for given use cases
* @throws IllegalStateException if not initialized
* @throws IllegalArgumentException if {@code newUseCaseConfigs} is an empty list, if
@@ -105,5 +109,5 @@
boolean isConcurrentCameraModeOn,
@NonNull String cameraId,
@NonNull List<AttachedSurfaceInfo> existingSurfaces,
- @NonNull List<UseCaseConfig<?>> newUseCaseConfigs);
+ @NonNull Map<UseCaseConfig<?>, List<Size>> newUseCaseConfigsSupportedSizeMap);
}
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/internal/CameraUseCaseAdapter.java b/camera/camera-core/src/main/java/androidx/camera/core/internal/CameraUseCaseAdapter.java
index 789f991..37db291 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/internal/CameraUseCaseAdapter.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/internal/CameraUseCaseAdapter.java
@@ -17,6 +17,7 @@
package androidx.camera.core.internal;
import static androidx.camera.core.impl.ImageFormatConstants.INTERNAL_DEFINED_IMAGE_FORMAT_PRIVATE;
+import static androidx.camera.core.impl.utils.TransformUtils.rectToSize;
import static androidx.core.util.Preconditions.checkArgument;
import static androidx.core.util.Preconditions.checkState;
@@ -525,9 +526,14 @@
suggestedStreamSpecs.put(useCase, useCase.getAttachedStreamSpec());
}
+ Rect sensorRect = ((CameraControlInternal) getCameraControl()).getSensorRect();
+ SupportedOutputSizesSorter supportedOutputSizesSorter = new SupportedOutputSizesSorter(
+ (CameraInfoInternal) getCameraInfo(), rectToSize(sensorRect));
+
// Calculate resolution for new use cases.
if (!newUseCases.isEmpty()) {
Map<UseCaseConfig<?>, UseCase> configToUseCaseMap = new HashMap<>();
+ Map<UseCaseConfig<?>, List<Size>> configToSupportedSizesMap = new HashMap<>();
for (UseCase useCase : newUseCases) {
ConfigPair configPair = configPairMap.get(useCase);
// Combine with default configuration.
@@ -535,6 +541,9 @@
useCase.mergeConfigs(cameraInfoInternal, configPair.mExtendedConfig,
configPair.mCameraConfig);
configToUseCaseMap.put(combinedUseCaseConfig, useCase);
+ configToSupportedSizesMap.put(combinedUseCaseConfig,
+ supportedOutputSizesSorter.getSortedSupportedOutputSizes(
+ combinedUseCaseConfig));
}
// Get suggested stream specifications and update the use case session configuration
@@ -542,7 +551,7 @@
mCameraDeviceSurfaceManager.getSuggestedStreamSpecs(
isConcurrentCameraModeOn,
cameraId, existingSurfaces,
- new ArrayList<>(configToUseCaseMap.keySet()));
+ configToSupportedSizesMap);
for (Map.Entry<UseCaseConfig<?>, UseCase> entry : configToUseCaseMap.entrySet()) {
suggestedStreamSpecs.put(entry.getValue(),
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/internal/SupportedOutputSizesSorter.java b/camera/camera-core/src/main/java/androidx/camera/core/internal/SupportedOutputSizesSorter.java
new file mode 100644
index 0000000..b8e80ef
--- /dev/null
+++ b/camera/camera-core/src/main/java/androidx/camera/core/internal/SupportedOutputSizesSorter.java
@@ -0,0 +1,424 @@
+/*
+ * Copyright 2023 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.core.internal;
+
+import static androidx.camera.core.impl.utils.AspectRatioUtil.ASPECT_RATIO_16_9;
+import static androidx.camera.core.impl.utils.AspectRatioUtil.ASPECT_RATIO_3_4;
+import static androidx.camera.core.impl.utils.AspectRatioUtil.ASPECT_RATIO_4_3;
+import static androidx.camera.core.impl.utils.AspectRatioUtil.ASPECT_RATIO_9_16;
+import static androidx.camera.core.impl.utils.AspectRatioUtil.hasMatchingAspectRatio;
+
+import android.util.Pair;
+import android.util.Rational;
+import android.util.Size;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
+import androidx.camera.core.AspectRatio;
+import androidx.camera.core.Logger;
+import androidx.camera.core.ResolutionSelector;
+import androidx.camera.core.impl.CameraInfoInternal;
+import androidx.camera.core.impl.ImageOutputConfig;
+import androidx.camera.core.impl.UseCaseConfig;
+import androidx.camera.core.impl.utils.AspectRatioUtil;
+import androidx.camera.core.impl.utils.CompareSizesByArea;
+import androidx.camera.core.internal.utils.SizeUtil;
+import androidx.core.util.Preconditions;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * A class used to sort the supported output sizes according to the use case configs
+ */
+@RequiresApi(21)
+class SupportedOutputSizesSorter {
+ private static final String TAG = "SupportedOutputSizesCollector";
+ private final CameraInfoInternal mCameraInfoInternal;
+ private final Size mActiveArraySize;
+ private final boolean mIsSensorLandscapeResolution;
+ private final SupportedOutputSizesSorterLegacy mSupportedOutputSizesSorterLegacy;
+
+ SupportedOutputSizesSorter(@NonNull CameraInfoInternal cameraInfoInternal,
+ @NonNull Size activeArraySize) {
+ mCameraInfoInternal = cameraInfoInternal;
+ mActiveArraySize = activeArraySize;
+ mIsSensorLandscapeResolution = mActiveArraySize.getWidth() >= mActiveArraySize.getHeight();
+ mSupportedOutputSizesSorterLegacy =
+ new SupportedOutputSizesSorterLegacy(cameraInfoInternal, activeArraySize);
+ }
+
+ @NonNull
+ List<Size> getSortedSupportedOutputSizes(@NonNull UseCaseConfig<?> useCaseConfig) {
+ ImageOutputConfig imageOutputConfig = (ImageOutputConfig) useCaseConfig;
+ List<Size> customOrderedResolutions = imageOutputConfig.getCustomOrderedResolutions(null);
+
+ // Directly returns the custom ordered resolutions list if it is set.
+ if (customOrderedResolutions != null) {
+ return customOrderedResolutions;
+ }
+
+ // Retrieves the resolution candidate list according to the use case config if
+ List<Size> resolutionCandidateList = getResolutionCandidateList(useCaseConfig);
+
+ ResolutionSelector resolutionSelector = imageOutputConfig.getResolutionSelector(null);
+
+ if (resolutionSelector == null) {
+ return mSupportedOutputSizesSorterLegacy.sortSupportedOutputSizes(
+ resolutionCandidateList, useCaseConfig);
+ } else {
+ Size miniBoundingSize = resolutionSelector.getPreferredResolution();
+ if (miniBoundingSize == null) {
+ miniBoundingSize = imageOutputConfig.getDefaultResolution(null);
+ }
+ return sortSupportedOutputSizesByResolutionSelector(resolutionCandidateList,
+ resolutionSelector, miniBoundingSize);
+ }
+ }
+
+ @NonNull
+ private List<Size> getResolutionCandidateList(@NonNull UseCaseConfig<?> useCaseConfig) {
+ int imageFormat = useCaseConfig.getInputFormat();
+ // Tries to get the custom supported resolutions list if it is set
+ List<Size> resolutionCandidateList = getCustomizedSupportedResolutionsFromConfig(
+ imageFormat, (ImageOutputConfig) useCaseConfig);
+
+ // Tries to get the supported output sizes from the CameraInfoInternal if both custom
+ // ordered and supported resolutions lists are not set.
+ if (resolutionCandidateList == null) {
+ resolutionCandidateList = mCameraInfoInternal.getSupportedResolutions(imageFormat);
+ }
+
+ return resolutionCandidateList;
+ }
+
+ /**
+ * Retrieves the customized supported resolutions from the use case config.
+ *
+ * <p>In some cases, the use case might not be able to use all the supported output sizes
+ * retrieved from the stream configuration map. For example, extensions is enabled. These
+ * sizes can be set in the use case config by
+ * {@link ImageOutputConfig.Builder#setSupportedResolutions(List)}. SupportedOutputSizesSorter
+ * should use the customized supported resolutions to run the sort/filter logic if it is set.
+ */
+ @Nullable
+ private List<Size> getCustomizedSupportedResolutionsFromConfig(int imageFormat,
+ @NonNull ImageOutputConfig config) {
+ Size[] outputSizes = null;
+
+ // Try to retrieve customized supported resolutions from config.
+ List<Pair<Integer, Size[]>> formatResolutionsPairList =
+ config.getSupportedResolutions(null);
+
+ if (formatResolutionsPairList != null) {
+ for (Pair<Integer, Size[]> formatResolutionPair : formatResolutionsPairList) {
+ if (formatResolutionPair.first == imageFormat) {
+ outputSizes = formatResolutionPair.second;
+ break;
+ }
+ }
+ }
+
+ return outputSizes == null ? null : Arrays.asList(outputSizes);
+ }
+
+ /**
+ * Sorts the resolution candidate list by the following steps:
+ *
+ * 1. Filters out the candidate list according to the max resolution.
+ * 2. Sorts the candidate list according to ResolutionSelector strategies.
+ */
+ @NonNull
+ private List<Size> sortSupportedOutputSizesByResolutionSelector(
+ @NonNull List<Size> resolutionCandidateList,
+ @NonNull ResolutionSelector resolutionSelector,
+ @Nullable Size miniBoundingSize) {
+ if (resolutionCandidateList.isEmpty()) {
+ return resolutionCandidateList;
+ }
+
+ List<Size> descendingSizeList = new ArrayList<>(resolutionCandidateList);
+
+ // Sort the result sizes. The Comparator result must be reversed to have a descending
+ // order result.
+ Collections.sort(descendingSizeList, new CompareSizesByArea(true));
+
+ // 1. Filters out the candidate list according to the min size bound and max resolution.
+ List<Size> filteredSizeList = filterOutResolutionCandidateListByMaxResolutionSetting(
+ descendingSizeList, resolutionSelector);
+
+ // 2. Sorts the candidate list according to the rules of new Resolution API.
+ return sortResolutionCandidateListByTargetAspectRatioAndResolutionSettings(
+ filteredSizeList, resolutionSelector, miniBoundingSize);
+
+ }
+
+ /**
+ * Filters out the resolution candidate list by the max resolution setting.
+ *
+ * The input size list should have been sorted in descending order.
+ */
+ private static List<Size> filterOutResolutionCandidateListByMaxResolutionSetting(
+ @NonNull List<Size> resolutionCandidateList,
+ @NonNull ResolutionSelector resolutionSelector) {
+ // Retrieves the max resolution setting. When ResolutionSelector is used, all resolution
+ // selection logic should depend on ResolutionSelector's settings.
+ Size maxResolution = resolutionSelector.getMaxResolution();
+
+ if (maxResolution == null) {
+ return resolutionCandidateList;
+ }
+
+ // Filter out the resolution candidate list by the max resolution. Sizes that any edge
+ // exceeds the max resolution will be filtered out.
+ List<Size> resultList = new ArrayList<>();
+ for (Size outputSize : resolutionCandidateList) {
+ if (!SizeUtil.isLongerInAnyEdge(outputSize, maxResolution)) {
+ resultList.add(outputSize);
+ }
+ }
+
+ if (resultList.isEmpty()) {
+ throw new IllegalArgumentException(
+ "Resolution candidate list is empty after filtering out by the settings!");
+ }
+
+ return resultList;
+ }
+
+ /**
+ * Sorts the resolution candidate list according to the new ResolutionSelector API logic.
+ *
+ * The list will be sorted by the following order:
+ * 1. size of preferred resolution
+ * 2. a resolution with preferred aspect ratio, is not smaller than, and is closest to the
+ * preferred resolution.
+ * 3. resolutions with preferred aspect ratio and is smaller than the preferred resolution
+ * size in descending order of resolution area size.
+ * 4. Other sizes sorted by CompareAspectRatiosByMappingAreaInFullFovAspectRatioSpace and
+ * area size.
+ */
+ @NonNull
+ private List<Size> sortResolutionCandidateListByTargetAspectRatioAndResolutionSettings(
+ @NonNull List<Size> resolutionCandidateList,
+ @NonNull ResolutionSelector resolutionSelector,
+ @Nullable Size miniBoundingSize) {
+ Rational aspectRatio = getTargetAspectRatioRationalValue(
+ resolutionSelector.getPreferredAspectRatio(), mIsSensorLandscapeResolution);
+ Preconditions.checkNotNull(aspectRatio, "ResolutionSelector should also have aspect ratio"
+ + " value.");
+
+ Size targetSize = resolutionSelector.getPreferredResolution();
+ List<Size> resultList = sortResolutionCandidateListByTargetAspectRatioAndSize(
+ resolutionCandidateList, aspectRatio, miniBoundingSize);
+
+ // Moves the target size to the first position if it exists in the resolution candidate
+ // list.
+ if (resultList.contains(targetSize)) {
+ resultList.remove(targetSize);
+ resultList.add(0, targetSize);
+ }
+
+ return resultList;
+ }
+
+ /**
+ * Sorts the resolution candidate list according to the target aspect ratio and size settings.
+ *
+ * 1. The resolution candidate list will be grouped by aspect ratio.
+ * 2. Moves the smallest size larger than the mini bounding size to the first position for each
+ * aspect ratio sizes group.
+ * 3. The aspect ratios of groups will be sorted against to the target aspect ratio setting by
+ * CompareAspectRatiosByMappingAreaInFullFovAspectRatioSpace.
+ * 4. Concatenate all sizes as the result list
+ */
+ @NonNull
+ private List<Size> sortResolutionCandidateListByTargetAspectRatioAndSize(
+ @NonNull List<Size> resolutionCandidateList, @NonNull Rational aspectRatio,
+ @Nullable Size miniBoundingSize) {
+ // Rearrange the supported size to put the ones with the same aspect ratio in the front
+ // of the list and put others in the end from large to small. Some low end devices may
+ // not able to get an supported resolution that match the preferred aspect ratio.
+
+ // Group output sizes by aspect ratio.
+ Map<Rational, List<Size>> aspectRatioSizeListMap =
+ groupSizesByAspectRatio(resolutionCandidateList);
+
+ // If the target resolution is set, use it to remove unnecessary larger sizes.
+ if (miniBoundingSize != null) {
+ // Sorts sizes from each aspect ratio size list
+ for (Rational key : aspectRatioSizeListMap.keySet()) {
+ List<Size> sortedResult = sortSupportedSizesByMiniBoundingSize(
+ aspectRatioSizeListMap.get(key), miniBoundingSize);
+ aspectRatioSizeListMap.put(key, sortedResult);
+ }
+ }
+
+ // Sort the aspect ratio key set by the target aspect ratio.
+ List<Rational> aspectRatios = new ArrayList<>(aspectRatioSizeListMap.keySet());
+ Rational fullFovRatio = mActiveArraySize != null ? new Rational(
+ mActiveArraySize.getWidth(), mActiveArraySize.getHeight()) : null;
+ Collections.sort(aspectRatios,
+ new AspectRatioUtil.CompareAspectRatiosByMappingAreaInFullFovAspectRatioSpace(
+ aspectRatio, fullFovRatio));
+
+ List<Size> resultList = new ArrayList<>();
+
+ // Put available sizes into final result list by aspect ratio distance to target ratio.
+ for (Rational rational : aspectRatios) {
+ for (Size size : aspectRatioSizeListMap.get(rational)) {
+ // A size may exist in multiple groups in mod16 condition. Keep only one in
+ // the final list.
+ if (!resultList.contains(size)) {
+ resultList.add(size);
+ }
+ }
+ }
+
+ return resultList;
+ }
+
+ /**
+ * Returns the target aspect ratio rational value according to the ResolutionSelector settings.
+ */
+ @Nullable
+ static Rational getTargetAspectRatioRationalValue(@AspectRatio.Ratio int aspectRatio,
+ boolean isSensorLandscapeResolution) {
+ Rational outputRatio = null;
+
+ switch (aspectRatio) {
+ case AspectRatio.RATIO_4_3:
+ outputRatio = isSensorLandscapeResolution ? ASPECT_RATIO_4_3
+ : ASPECT_RATIO_3_4;
+ break;
+ case AspectRatio.RATIO_16_9:
+ outputRatio = isSensorLandscapeResolution ? ASPECT_RATIO_16_9
+ : ASPECT_RATIO_9_16;
+ break;
+ case AspectRatio.RATIO_DEFAULT:
+ break;
+ default:
+ Logger.e(TAG, "Undefined target aspect ratio: " + aspectRatio);
+ }
+
+ return outputRatio;
+ }
+
+ /**
+ * Returns the grouping aspect ratio keys of the input resolution list.
+ *
+ * <p>Some sizes might be mod16 case. When grouping, those sizes will be grouped into an
+ * existing aspect ratio group if the aspect ratio can match by the mod16 rule.
+ */
+ @NonNull
+ static List<Rational> getResolutionListGroupingAspectRatioKeys(
+ @NonNull List<Size> resolutionCandidateList) {
+ List<Rational> aspectRatios = new ArrayList<>();
+
+ // Adds the default 4:3 and 16:9 items first to avoid their mod16 sizes to create
+ // additional items.
+ aspectRatios.add(ASPECT_RATIO_4_3);
+ aspectRatios.add(ASPECT_RATIO_16_9);
+
+ // Tries to find the aspect ratio which the target size belongs to.
+ for (Size size : resolutionCandidateList) {
+ Rational newRatio = new Rational(size.getWidth(), size.getHeight());
+ boolean aspectRatioFound = aspectRatios.contains(newRatio);
+
+ // The checking size might be a mod16 size which can be mapped to an existing aspect
+ // ratio group.
+ if (!aspectRatioFound) {
+ boolean hasMatchingAspectRatio = false;
+ for (Rational aspectRatio : aspectRatios) {
+ if (hasMatchingAspectRatio(size, aspectRatio)) {
+ hasMatchingAspectRatio = true;
+ break;
+ }
+ }
+ if (!hasMatchingAspectRatio) {
+ aspectRatios.add(newRatio);
+ }
+ }
+ }
+
+ return aspectRatios;
+ }
+
+ /**
+ * Removes unnecessary sizes by target size.
+ *
+ * <p>If the target resolution is set, a size that is equal to or closest to the target
+ * resolution will be selected. If the list includes more than one size equal to or larger
+ * than the target resolution, only one closest size needs to be kept. The other larger sizes
+ * can be removed so that they won't be selected to use.
+ *
+ * @param supportedSizesList The list should have been sorted in descending order.
+ * @param miniBoundingSize The target size used to remove unnecessary sizes.
+ */
+ static List<Size> sortSupportedSizesByMiniBoundingSize(@NonNull List<Size> supportedSizesList,
+ @NonNull Size miniBoundingSize) {
+ if (supportedSizesList.isEmpty()) {
+ return supportedSizesList;
+ }
+
+ List<Size> resultList = new ArrayList<>();
+
+ // Get the index of the item that is equal to or closest to the target size.
+ for (int i = 0; i < supportedSizesList.size(); i++) {
+ Size outputSize = supportedSizesList.get(i);
+ if (outputSize.getWidth() >= miniBoundingSize.getWidth()
+ && outputSize.getHeight() >= miniBoundingSize.getHeight()) {
+ // The supportedSizesList is in descending order. Checking and put the
+ // mini-bounding-above size at position 0 so that the smallest larger resolution
+ // will be put in the first position finally.
+ resultList.add(0, outputSize);
+ } else {
+ // Appends the remaining smaller sizes in descending order.
+ resultList.add(outputSize);
+ }
+ }
+
+ return resultList;
+ }
+
+ static Map<Rational, List<Size>> groupSizesByAspectRatio(@NonNull List<Size> sizes) {
+ Map<Rational, List<Size>> aspectRatioSizeListMap = new HashMap<>();
+
+ List<Rational> aspectRatioKeys = getResolutionListGroupingAspectRatioKeys(sizes);
+
+ for (Rational aspectRatio : aspectRatioKeys) {
+ aspectRatioSizeListMap.put(aspectRatio, new ArrayList<>());
+ }
+
+ for (Size outputSize : sizes) {
+ for (Rational key : aspectRatioSizeListMap.keySet()) {
+ // Put the size into all groups that is matched in mod16 condition since a size
+ // may match multiple aspect ratio in mod16 algorithm.
+ if (hasMatchingAspectRatio(outputSize, key)) {
+ aspectRatioSizeListMap.get(key).add(outputSize);
+ }
+ }
+ }
+
+ return aspectRatioSizeListMap;
+ }
+}
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/internal/SupportedOutputSizesSorterLegacy.java b/camera/camera-core/src/main/java/androidx/camera/core/internal/SupportedOutputSizesSorterLegacy.java
new file mode 100644
index 0000000..674a6a4
--- /dev/null
+++ b/camera/camera-core/src/main/java/androidx/camera/core/internal/SupportedOutputSizesSorterLegacy.java
@@ -0,0 +1,275 @@
+/*
+ * Copyright 2023 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.core.internal;
+
+import static androidx.camera.core.impl.utils.AspectRatioUtil.hasMatchingAspectRatio;
+import static androidx.camera.core.internal.SupportedOutputSizesSorter.getResolutionListGroupingAspectRatioKeys;
+import static androidx.camera.core.internal.SupportedOutputSizesSorter.groupSizesByAspectRatio;
+import static androidx.camera.core.internal.SupportedOutputSizesSorter.sortSupportedSizesByMiniBoundingSize;
+import static androidx.camera.core.internal.utils.SizeUtil.RESOLUTION_VGA;
+import static androidx.camera.core.internal.utils.SizeUtil.RESOLUTION_ZERO;
+import static androidx.camera.core.internal.utils.SizeUtil.getArea;
+
+import android.hardware.camera2.CameraCharacteristics;
+import android.util.Rational;
+import android.util.Size;
+import android.view.Surface;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
+import androidx.camera.core.AspectRatio;
+import androidx.camera.core.impl.CameraInfoInternal;
+import androidx.camera.core.impl.ImageOutputConfig;
+import androidx.camera.core.impl.UseCaseConfig;
+import androidx.camera.core.impl.utils.AspectRatioUtil;
+import androidx.camera.core.impl.utils.CameraOrientationUtil;
+import androidx.camera.core.impl.utils.CompareSizesByArea;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * A class used to sort the supported output sizes according to the legacy use case configs
+ */
+@RequiresApi(21)
+class SupportedOutputSizesSorterLegacy {
+ private static final String TAG = "SupportedOutputSizesCollector";
+ private final int mSensorOrientation;
+ private final int mLensFacing;
+ private final Size mActiveArraySize;
+ private final boolean mIsSensorLandscapeResolution;
+
+ SupportedOutputSizesSorterLegacy(@NonNull CameraInfoInternal cameraInfoInternal,
+ @NonNull Size activeArraySize) {
+ mSensorOrientation = cameraInfoInternal.getSensorRotationDegrees();
+ mLensFacing = cameraInfoInternal.getLensFacing();
+ mActiveArraySize = activeArraySize;
+ mIsSensorLandscapeResolution = mActiveArraySize.getWidth() >= mActiveArraySize.getHeight();
+ }
+
+ /**
+ * Sorts the resolution candidate list by the following steps:
+ *
+ * 1. Filters out the candidate list according to the mini and max resolution.
+ * 2. Sorts the candidate list according to legacy target aspect ratio or resolution settings.
+ */
+ @NonNull
+ List<Size> sortSupportedOutputSizes(
+ @NonNull List<Size> resolutionCandidateList,
+ @NonNull UseCaseConfig<?> useCaseConfig) {
+ if (resolutionCandidateList.isEmpty()) {
+ return resolutionCandidateList;
+ }
+
+ List<Size> descendingSizeList = new ArrayList<>(resolutionCandidateList);
+
+ // Sort the result sizes. The Comparator result must be reversed to have a descending
+ // order result.
+ Collections.sort(descendingSizeList, new CompareSizesByArea(true));
+
+ List<Size> filteredSizeList = new ArrayList<>();
+ ImageOutputConfig imageOutputConfig = (ImageOutputConfig) useCaseConfig;
+ Size maxSize = imageOutputConfig.getMaxResolution(null);
+ Size maxSupportedOutputSize = descendingSizeList.get(0);
+
+ // Set maxSize as the max resolution setting or the max supported output size for the
+ // image format, whichever is smaller.
+ if (maxSize == null || getArea(maxSupportedOutputSize) < getArea(maxSize)) {
+ maxSize = maxSupportedOutputSize;
+ }
+
+ Size targetSize = getTargetSize(imageOutputConfig);
+ Size minSize = RESOLUTION_VGA;
+ int defaultSizeArea = getArea(RESOLUTION_VGA);
+ int maxSizeArea = getArea(maxSize);
+ // When maxSize is smaller than 640x480, set minSize as 0x0. It means the min size bound
+ // will be ignored. Otherwise, set the minimal size according to min(DEFAULT_SIZE,
+ // TARGET_RESOLUTION).
+ if (maxSizeArea < defaultSizeArea) {
+ minSize = RESOLUTION_ZERO;
+ } else if (targetSize != null && getArea(targetSize) < defaultSizeArea) {
+ minSize = targetSize;
+ }
+
+ // Filter out the ones that exceed the maximum size and the minimum size. The output
+ // sizes candidates list won't have duplicated items.
+ for (Size outputSize : descendingSizeList) {
+ if (getArea(outputSize) <= getArea(maxSize) && getArea(outputSize) >= getArea(minSize)
+ && !filteredSizeList.contains(outputSize)) {
+ filteredSizeList.add(outputSize);
+ }
+ }
+
+ if (filteredSizeList.isEmpty()) {
+ throw new IllegalArgumentException(
+ "All supported output sizes are filtered out according to current resolution "
+ + "selection settings.");
+ }
+
+ Rational aspectRatio = getTargetAspectRatioByLegacyApi(imageOutputConfig, filteredSizeList);
+
+ // Check the default resolution if the target resolution is not set
+ targetSize = targetSize == null ? imageOutputConfig.getDefaultResolution(null) : targetSize;
+
+ List<Size> resultSizeList = new ArrayList<>();
+ Map<Rational, List<Size>> aspectRatioSizeListMap = new HashMap<>();
+
+ if (aspectRatio == null) {
+ // If no target aspect ratio is set, all sizes can be added to the result list
+ // directly. No need to sort again since the source list has been sorted previously.
+ resultSizeList.addAll(filteredSizeList);
+
+ // If the target resolution is set, use it to sort the sizes list.
+ if (targetSize != null) {
+ resultSizeList = sortSupportedSizesByMiniBoundingSize(resultSizeList, targetSize);
+ }
+ } else {
+ // Rearrange the supported size to put the ones with the same aspect ratio in the front
+ // of the list and put others in the end from large to small. Some low end devices may
+ // not able to get an supported resolution that match the preferred aspect ratio.
+
+ // Group output sizes by aspect ratio.
+ aspectRatioSizeListMap = groupSizesByAspectRatio(filteredSizeList);
+
+ // If the target resolution is set, use it to remove unnecessary larger sizes.
+ if (targetSize != null) {
+ // Remove unnecessary larger sizes from each aspect ratio size list
+ for (Rational key : aspectRatioSizeListMap.keySet()) {
+ List<Size> sortedResult = sortSupportedSizesByMiniBoundingSize(
+ aspectRatioSizeListMap.get(key), targetSize);
+ aspectRatioSizeListMap.put(key, sortedResult);
+ }
+ }
+
+ // Sort the aspect ratio key set by the target aspect ratio.
+ List<Rational> aspectRatios = new ArrayList<>(aspectRatioSizeListMap.keySet());
+ Rational fullFovRatio = mActiveArraySize != null ? new Rational(
+ mActiveArraySize.getWidth(), mActiveArraySize.getHeight()) : null;
+ Collections.sort(aspectRatios,
+ new AspectRatioUtil.CompareAspectRatiosByMappingAreaInFullFovAspectRatioSpace(
+ aspectRatio, fullFovRatio));
+
+ // Put available sizes into final result list by aspect ratio distance to target ratio.
+ for (Rational rational : aspectRatios) {
+ for (Size size : aspectRatioSizeListMap.get(rational)) {
+ // A size may exist in multiple groups in mod16 condition. Keep only one in
+ // the final list.
+ if (!resultSizeList.contains(size)) {
+ resultSizeList.add(size);
+ }
+ }
+ }
+ }
+
+ return resultSizeList;
+ }
+
+ /**
+ * Returns the target aspect ratio rational value according to the legacy API settings.
+ */
+ private Rational getTargetAspectRatioByLegacyApi(@NonNull ImageOutputConfig imageOutputConfig,
+ @NonNull List<Size> resolutionCandidateList) {
+ Rational outputRatio = null;
+
+ if (imageOutputConfig.hasTargetAspectRatio()) {
+ @AspectRatio.Ratio int aspectRatio = imageOutputConfig.getTargetAspectRatio();
+ outputRatio = SupportedOutputSizesSorter.getTargetAspectRatioRationalValue(aspectRatio,
+ mIsSensorLandscapeResolution);
+ } else {
+ // The legacy resolution API will use the aspect ratio of the target size to
+ // be the fallback target aspect ratio value when the use case has no target
+ // aspect ratio setting.
+ Size targetSize = getTargetSize(imageOutputConfig);
+ if (targetSize != null) {
+ outputRatio = getAspectRatioGroupKeyOfTargetSize(targetSize,
+ resolutionCandidateList);
+ }
+ }
+
+ return outputRatio;
+ }
+
+ @Nullable
+ private Size getTargetSize(@NonNull ImageOutputConfig imageOutputConfig) {
+ int targetRotation = imageOutputConfig.getTargetRotation(Surface.ROTATION_0);
+ // Calibrate targetSize by the target rotation value.
+ Size targetSize = imageOutputConfig.getTargetResolution(null);
+ targetSize = flipSizeByRotation(targetSize, targetRotation, mLensFacing,
+ mSensorOrientation);
+ return targetSize;
+ }
+
+ /**
+ * Returns the aspect ratio group key of the target size when grouping the input resolution
+ * candidate list.
+ *
+ * The resolution candidate list will be grouped with mod 16 consideration. Therefore, we
+ * also need to consider the mod 16 factor to find which aspect ratio of group the target size
+ * might be put in. So that sizes of the group will be selected to use in the highest priority.
+ */
+ @Nullable
+ private static Rational getAspectRatioGroupKeyOfTargetSize(@Nullable Size targetSize,
+ @NonNull List<Size> resolutionCandidateList) {
+ if (targetSize == null) {
+ return null;
+ }
+
+ List<Rational> aspectRatios = getResolutionListGroupingAspectRatioKeys(
+ resolutionCandidateList);
+
+ for (Rational aspectRatio : aspectRatios) {
+ if (hasMatchingAspectRatio(targetSize, aspectRatio)) {
+ return aspectRatio;
+ }
+ }
+
+ return new Rational(targetSize.getWidth(), targetSize.getHeight());
+ }
+
+ // Use target rotation to calibrate the size.
+ @Nullable
+ private static Size flipSizeByRotation(@Nullable Size size, int targetRotation, int lensFacing,
+ int sensorOrientation) {
+ Size outputSize = size;
+ // Calibrates the size with the display and sensor rotation degrees values.
+ if (size != null && isRotationNeeded(targetRotation, lensFacing, sensorOrientation)) {
+ outputSize = new Size(/* width= */size.getHeight(), /* height= */size.getWidth());
+ }
+ return outputSize;
+ }
+
+ private static boolean isRotationNeeded(int targetRotation, int lensFacing,
+ int sensorOrientation) {
+ int relativeRotationDegrees =
+ CameraOrientationUtil.surfaceRotationToDegrees(targetRotation);
+
+ // Currently this assumes that a back-facing camera is always opposite to the screen.
+ // This may not be the case for all devices, so in the future we may need to handle that
+ // scenario.
+ boolean isOppositeFacingScreen = CameraCharacteristics.LENS_FACING_BACK == lensFacing;
+
+ int sensorRotationDegrees = CameraOrientationUtil.getRelativeImageRotation(
+ relativeRotationDegrees,
+ sensorOrientation,
+ isOppositeFacingScreen);
+ return sensorRotationDegrees == 90 || sensorRotationDegrees == 270;
+ }
+}
diff --git a/camera/camera-core/src/test/java/androidx/camera/core/internal/SupportedOutputSizesSorterLegacyTest.kt b/camera/camera-core/src/test/java/androidx/camera/core/internal/SupportedOutputSizesSorterLegacyTest.kt
new file mode 100644
index 0000000..1304995
--- /dev/null
+++ b/camera/camera-core/src/test/java/androidx/camera/core/internal/SupportedOutputSizesSorterLegacyTest.kt
@@ -0,0 +1,390 @@
+/*
+ * Copyright 2023 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.core.internal
+
+import android.graphics.ImageFormat
+import android.os.Build
+import android.util.Size
+import android.view.Surface
+import androidx.camera.core.AspectRatio
+import androidx.camera.core.impl.UseCaseConfig
+import androidx.camera.core.impl.UseCaseConfigFactory.CaptureType
+import androidx.camera.core.impl.utils.CompareSizesByArea
+import androidx.camera.testing.fakes.FakeCameraInfoInternal
+import androidx.camera.testing.fakes.FakeUseCaseConfig
+import com.google.common.truth.Truth.assertThat
+import java.util.Arrays
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.RobolectricTestRunner
+import org.robolectric.annotation.internal.DoNotInstrument
+
+private const val TARGET_ASPECT_RATIO_NONE = -99
+private val LANDSCAPE_ACTIVE_ARRAY_SIZE = Size(4032, 3024)
+private val DEFAULT_SUPPORTED_SIZES = listOf(
+ 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
+)
+
+/**
+ * Unit tests for [SupportedOutputSizesSorterLegacy].
+ */
+@RunWith(RobolectricTestRunner::class)
+@DoNotInstrument
+@org.robolectric.annotation.Config(minSdk = Build.VERSION_CODES.LOLLIPOP)
+class SupportedOutputSizesSorterLegacyTest {
+ private val cameraInfoInternal = FakeCameraInfoInternal()
+ private val supportedOutputSizesSorterLegacy =
+ SupportedOutputSizesSorterLegacy(cameraInfoInternal, LANDSCAPE_ACTIVE_ARRAY_SIZE)
+
+ @Test
+ fun checkFilterOutSmallSizesByDefaultSize640x480() {
+ val resultList = supportedOutputSizesSorterLegacy.sortSupportedOutputSizes(
+ DEFAULT_SUPPORTED_SIZES,
+ createUseCaseConfig()
+ )
+ // Sizes smaller than 640x480 are filtered out by default
+ assertThat(resultList).containsNoneIn(
+ arrayOf(
+ Size(320, 240),
+ Size(320, 180),
+ Size(256, 144)
+ )
+ )
+ }
+
+ @Test
+ fun canPrioritize4x3SizesByTargetAspectRatio() {
+ val useCaseConfig = createUseCaseConfig(targetAspectRatio = AspectRatio.RATIO_4_3)
+ val resultList = supportedOutputSizesSorterLegacy.sortSupportedOutputSizes(
+ DEFAULT_SUPPORTED_SIZES,
+ useCaseConfig
+ )
+ // 4:3 items are prioritized in the result list
+ assertThat(resultList).containsExactlyElementsIn(
+ arrayOf(
+ // 4:3 items
+ Size(4032, 3024),
+ Size(1920, 1440),
+ Size(1280, 960),
+ Size(640, 480),
+ // 16:9 items
+ Size(3840, 2160),
+ Size(1920, 1080),
+ Size(1280, 720),
+ Size(960, 544),
+ Size(800, 450),
+ )
+ ).inOrder()
+ }
+
+ @Test
+ fun canPrioritize16x9SizesByTargetAspectRatio() {
+ val useCaseConfig = createUseCaseConfig(targetAspectRatio = AspectRatio.RATIO_16_9)
+ val resultList = supportedOutputSizesSorterLegacy.sortSupportedOutputSizes(
+ DEFAULT_SUPPORTED_SIZES,
+ useCaseConfig
+ )
+ // 16:9 items are prioritized in the result list
+ assertThat(resultList).containsExactlyElementsIn(
+ arrayOf(
+ // 16:9 items
+ Size(3840, 2160),
+ Size(1920, 1080),
+ Size(1280, 720),
+ Size(960, 544),
+ Size(800, 450),
+ // 4:3 items
+ Size(4032, 3024),
+ Size(1920, 1440),
+ Size(1280, 960),
+ Size(640, 480),
+ )
+ ).inOrder()
+ }
+
+ @Test
+ fun canPrioritize4x3SizesByTargetResolution() {
+ val useCaseConfig = createUseCaseConfig(targetResolution = Size(1280, 960))
+ val resultList = supportedOutputSizesSorterLegacy.sortSupportedOutputSizes(
+ DEFAULT_SUPPORTED_SIZES,
+ useCaseConfig
+ )
+ // 4:3 items are prioritized in the result list
+ assertThat(resultList).containsExactlyElementsIn(
+ arrayOf(
+ // 4:3 items
+ Size(1280, 960),
+ Size(1920, 1440),
+ Size(4032, 3024),
+ Size(640, 480),
+ // 16:9 items
+ Size(1920, 1080),
+ Size(3840, 2160),
+ Size(1280, 720),
+ Size(960, 544),
+ Size(800, 450),
+ )
+ ).inOrder()
+ }
+
+ @Test
+ fun canPrioritize16x9SizesByTargetResolution() {
+ val useCaseConfig = createUseCaseConfig(targetResolution = Size(1920, 1080))
+ val resultList = supportedOutputSizesSorterLegacy.sortSupportedOutputSizes(
+ DEFAULT_SUPPORTED_SIZES,
+ useCaseConfig
+ )
+ // 16:9 items are prioritized in the result list
+ assertThat(resultList).containsExactlyElementsIn(
+ arrayOf(
+ // 16:9 items
+ Size(1920, 1080),
+ Size(3840, 2160),
+ Size(1280, 720),
+ Size(960, 544),
+ Size(800, 450),
+ // 4:3 items
+ Size(1920, 1440),
+ Size(4032, 3024),
+ Size(1280, 960),
+ Size(640, 480),
+ )
+ ).inOrder()
+ }
+
+ @Test
+ fun canSelectSmallSizesByTargetResolutionSmallerThan640x480() {
+ val useCaseConfig = createUseCaseConfig(targetResolution = Size(320, 240))
+ val resultList = supportedOutputSizesSorterLegacy.sortSupportedOutputSizes(
+ DEFAULT_SUPPORTED_SIZES,
+ useCaseConfig
+ )
+ // 16:9 items are prioritized in the result list
+ assertThat(resultList).containsExactlyElementsIn(
+ arrayOf(
+ // 4:3 items
+ Size(320, 240),
+ Size(640, 480),
+ Size(1280, 960),
+ Size(1920, 1440),
+ Size(4032, 3024),
+ // 16:9 items
+ Size(800, 450),
+ Size(960, 544),
+ Size(1280, 720),
+ Size(1920, 1080),
+ Size(3840, 2160),
+ )
+ ).inOrder()
+ }
+
+ @Test
+ fun canPrioritizeSizesByResolutionOfUncommonAspectRatio() {
+ val useCaseConfig = createUseCaseConfig(targetResolution = Size(1280, 640))
+ val resultList = supportedOutputSizesSorterLegacy.sortSupportedOutputSizes(
+ DEFAULT_SUPPORTED_SIZES,
+ useCaseConfig
+ )
+ // 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.
+ assertThat(resultList).containsExactlyElementsIn(
+ arrayOf(
+ // 16:9 items
+ Size(1280, 720),
+ Size(1920, 1080),
+ Size(3840, 2160),
+ Size(960, 544),
+ Size(800, 450),
+ // 4:3 items
+ Size(1280, 960),
+ Size(1920, 1440),
+ Size(4032, 3024),
+ Size(640, 480),
+ )
+ ).inOrder()
+ }
+
+ @Test
+ fun checkFilterOutLargeSizesByMaxResolution() {
+ val useCaseConfig = createUseCaseConfig(maxResolution = Size(1920, 1080))
+ val resultList = supportedOutputSizesSorterLegacy.sortSupportedOutputSizes(
+ DEFAULT_SUPPORTED_SIZES,
+ useCaseConfig
+ )
+ assertThat(resultList).containsExactlyElementsIn(
+ arrayOf(
+ Size(1920, 1080),
+ Size(1280, 960),
+ Size(1280, 720),
+ Size(960, 544),
+ Size(800, 450),
+ Size(640, 480),
+ )
+ ).inOrder()
+ }
+
+ @Test
+ fun canKeepSmallSizesBySmallMaxResolution() {
+ val useCaseConfig = createUseCaseConfig(maxResolution = Size(320, 240))
+ val resultList = supportedOutputSizesSorterLegacy.sortSupportedOutputSizes(
+ DEFAULT_SUPPORTED_SIZES,
+ useCaseConfig
+ )
+ assertThat(resultList).containsExactlyElementsIn(
+ arrayOf(
+ Size(320, 240),
+ Size(320, 180),
+ Size(256, 144),
+ )
+ ).inOrder()
+ }
+
+ @Test
+ fun canSelectCorrect4x3SizeByDefaultResolution() {
+ val useCaseConfig = createUseCaseConfig(
+ targetAspectRatio = AspectRatio.RATIO_4_3,
+ defaultResolution = Size(640, 480)
+ )
+ val resultList = supportedOutputSizesSorterLegacy.sortSupportedOutputSizes(
+ DEFAULT_SUPPORTED_SIZES,
+ useCaseConfig
+ )
+ // The default resolution 640x480 will be used as target resolution to filter out too-large
+ // items in each aspect ratio group
+ assertThat(resultList).containsExactlyElementsIn(
+ arrayOf(
+ // 4:3 items
+ Size(640, 480),
+ Size(1280, 960),
+ Size(1920, 1440),
+ Size(4032, 3024),
+ // 16:9 items
+ Size(960, 544),
+ Size(1280, 720),
+ Size(1920, 1080),
+ Size(3840, 2160),
+ Size(800, 450),
+ )
+ ).inOrder()
+ }
+
+ @Test
+ fun canSelectCorrect16x9SizeByDefaultResolution() {
+ val useCaseConfig = createUseCaseConfig(
+ targetAspectRatio = AspectRatio.RATIO_16_9,
+ defaultResolution = Size(640, 480)
+ )
+ val resultList = supportedOutputSizesSorterLegacy.sortSupportedOutputSizes(
+ DEFAULT_SUPPORTED_SIZES,
+ useCaseConfig
+ )
+ // The default resolution 640x480 will be used as target resolution to filter out too-large
+ // items in each aspect ratio group
+ assertThat(resultList).containsExactlyElementsIn(
+ arrayOf(
+ // 16:9 items
+ Size(960, 544),
+ Size(1280, 720),
+ Size(1920, 1080),
+ Size(3840, 2160),
+ Size(800, 450),
+ // 4:3 items
+ Size(640, 480),
+ Size(1280, 960),
+ Size(1920, 1440),
+ Size(4032, 3024),
+ )
+ ).inOrder()
+ }
+
+ @Test
+ fun targetResolutionCanOverrideDefaultResolution() {
+ val useCaseConfig = createUseCaseConfig(
+ targetResolution = Size(1920, 1080),
+ defaultResolution = Size(640, 480)
+ )
+ val resultList = supportedOutputSizesSorterLegacy.sortSupportedOutputSizes(
+ DEFAULT_SUPPORTED_SIZES,
+ useCaseConfig
+ )
+ // The default resolution 640x480 will be used as target resolution to filter out too-large
+ // items in each aspect ratio group
+ assertThat(resultList).containsExactlyElementsIn(
+ arrayOf(
+ // 16:9 items
+ Size(1920, 1080),
+ Size(3840, 2160),
+ Size(1280, 720),
+ Size(960, 544),
+ Size(800, 450),
+ // 4:3 items
+ Size(1920, 1440),
+ Size(4032, 3024),
+ Size(1280, 960),
+ Size(640, 480),
+ )
+ ).inOrder()
+ }
+
+ @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(sizes.toList()).containsExactlyElementsIn(DEFAULT_SUPPORTED_SIZES).inOrder()
+ }
+
+ private fun createUseCaseConfig(
+ captureType: CaptureType = CaptureType.IMAGE_CAPTURE,
+ targetRotation: Int = Surface.ROTATION_0,
+ targetAspectRatio: Int = TARGET_ASPECT_RATIO_NONE,
+ targetResolution: Size? = null,
+ maxResolution: Size? = null,
+ defaultResolution: Size? = null,
+ ): UseCaseConfig<*> {
+ val builder = FakeUseCaseConfig.Builder(captureType, ImageFormat.JPEG)
+ builder.setTargetRotation(targetRotation)
+ if (targetAspectRatio != TARGET_ASPECT_RATIO_NONE) {
+ builder.setTargetAspectRatio(targetAspectRatio)
+ }
+ targetResolution?.let { builder.setTargetResolution(it) }
+ maxResolution?.let { builder.setMaxResolution(it) }
+ defaultResolution?.let { builder.setDefaultResolution(it) }
+ return builder.useCaseConfig
+ }
+}
\ No newline at end of file
diff --git a/camera/camera-core/src/test/java/androidx/camera/core/internal/SupportedOutputSizesSorterTest.kt b/camera/camera-core/src/test/java/androidx/camera/core/internal/SupportedOutputSizesSorterTest.kt
new file mode 100644
index 0000000..fd9e4ea
--- /dev/null
+++ b/camera/camera-core/src/test/java/androidx/camera/core/internal/SupportedOutputSizesSorterTest.kt
@@ -0,0 +1,127 @@
+/*
+ * Copyright 2023 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.core.internal
+
+import android.graphics.ImageFormat
+import android.os.Build
+import android.util.Pair
+import android.util.Size
+import androidx.camera.core.impl.UseCaseConfigFactory.CaptureType
+import androidx.camera.testing.fakes.FakeCameraInfoInternal
+import androidx.camera.testing.fakes.FakeUseCaseConfig
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.RobolectricTestRunner
+import org.robolectric.annotation.internal.DoNotInstrument
+
+private val LANDSCAPE_ACTIVE_ARRAY_SIZE = Size(4032, 3024)
+private val DEFAULT_SUPPORTED_SIZES = listOf(
+ 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
+)
+
+/**
+ * Unit tests for [SupportedOutputSizesSorter].
+ */
+@RunWith(RobolectricTestRunner::class)
+@DoNotInstrument
+@org.robolectric.annotation.Config(minSdk = Build.VERSION_CODES.LOLLIPOP)
+class SupportedOutputSizesSorterTest {
+
+ @Test
+ fun canSelectCustomOrderedResolutions() {
+ // Arrange
+ val imageFormat = ImageFormat.JPEG
+ val cameraInfoInternal = FakeCameraInfoInternal().apply {
+ setSupportedResolutions(imageFormat, DEFAULT_SUPPORTED_SIZES)
+ }
+ val supportedOutputSizesSorter =
+ SupportedOutputSizesSorter(cameraInfoInternal, LANDSCAPE_ACTIVE_ARRAY_SIZE)
+ // Sets up the custom ordered resolutions
+ val useCaseConfig =
+ FakeUseCaseConfig.Builder(CaptureType.IMAGE_CAPTURE, imageFormat).apply {
+ setCustomOrderedResolutions(
+ listOf(
+ Size(1920, 1080),
+ Size(720, 480),
+ Size(640, 480)
+ )
+ )
+ }.useCaseConfig
+
+ // Act
+ val sortedResult = supportedOutputSizesSorter.getSortedSupportedOutputSizes(useCaseConfig)
+
+ // Assert
+ assertThat(sortedResult).containsExactlyElementsIn(
+ listOf(
+ Size(1920, 1080),
+ Size(720, 480),
+ Size(640, 480)
+ )
+ ).inOrder()
+ }
+
+ @Test
+ fun canSelectCustomSupportedResolutions() {
+ // Arrange
+ val imageFormat = ImageFormat.JPEG
+ val cameraInfoInternal = FakeCameraInfoInternal().apply {
+ setSupportedResolutions(imageFormat, DEFAULT_SUPPORTED_SIZES)
+ }
+ val supportedOutputSizesSorter =
+ SupportedOutputSizesSorter(cameraInfoInternal, LANDSCAPE_ACTIVE_ARRAY_SIZE)
+ // Sets up the custom supported resolutions
+ val useCaseConfig =
+ FakeUseCaseConfig.Builder(CaptureType.IMAGE_CAPTURE, imageFormat).apply {
+ setSupportedResolutions(
+ listOf(
+ Pair.create(
+ imageFormat, arrayOf(
+ Size(1920, 1080),
+ Size(720, 480),
+ Size(640, 480)
+ )
+ )
+ )
+ )
+ }.useCaseConfig
+
+ // Act
+ val sortedResult = supportedOutputSizesSorter.getSortedSupportedOutputSizes(useCaseConfig)
+
+ // Assert
+ assertThat(sortedResult).containsExactlyElementsIn(
+ listOf(
+ Size(1920, 1080),
+ Size(720, 480),
+ Size(640, 480)
+ )
+ ).inOrder()
+ }
+}
\ No newline at end of file
diff --git a/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeCameraDeviceSurfaceManager.java b/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeCameraDeviceSurfaceManager.java
index ef49742..cd5d637 100644
--- a/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeCameraDeviceSurfaceManager.java
+++ b/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeCameraDeviceSurfaceManager.java
@@ -89,13 +89,14 @@
SurfaceConfig.ConfigSize.PREVIEW);
}
- @Override
@NonNull
+ @Override
public Map<UseCaseConfig<?>, StreamSpec> getSuggestedStreamSpecs(
- boolean isConcurrentCameraModeOn,
- @NonNull String cameraId,
+ boolean isConcurrentCameraModeOn, @NonNull String cameraId,
@NonNull List<AttachedSurfaceInfo> existingSurfaces,
- @NonNull List<UseCaseConfig<?>> newUseCaseConfigs) {
+ @NonNull Map<UseCaseConfig<?>, List<Size>> newUseCaseConfigsSupportedSizeMap) {
+ List<UseCaseConfig<?>> newUseCaseConfigs =
+ new ArrayList<>(newUseCaseConfigsSupportedSizeMap.keySet());
checkSurfaceCombo(existingSurfaces, newUseCaseConfigs);
Map<UseCaseConfig<?>, StreamSpec> suggestedStreamSpecs = new HashMap<>();
for (UseCaseConfig<?> useCaseConfig : newUseCaseConfigs) {
diff --git a/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeUseCaseConfig.java b/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeUseCaseConfig.java
index 931d5f1..40ce0b7 100644
--- a/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeUseCaseConfig.java
+++ b/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeUseCaseConfig.java
@@ -16,6 +16,8 @@
package androidx.camera.testing.fakes;
+import static androidx.camera.core.impl.ImageFormatConstants.INTERNAL_DEFINED_IMAGE_FORMAT_PRIVATE;
+
import android.util.Pair;
import android.util.Size;
@@ -26,7 +28,6 @@
import androidx.camera.core.UseCase;
import androidx.camera.core.impl.CaptureConfig;
import androidx.camera.core.impl.Config;
-import androidx.camera.core.impl.ImageFormatConstants;
import androidx.camera.core.impl.ImageOutputConfig;
import androidx.camera.core.impl.MutableConfig;
import androidx.camera.core.impl.MutableOptionsBundle;
@@ -57,7 +58,7 @@
@Override
public int getInputFormat() {
return retrieveOption(OPTION_INPUT_FORMAT,
- ImageFormatConstants.INTERNAL_DEFINED_IMAGE_FORMAT_PRIVATE);
+ INTERNAL_DEFINED_IMAGE_FORMAT_PRIVATE);
}
/** Builder for an empty Config */
@@ -81,6 +82,11 @@
this(MutableOptionsBundle.create(), captureType);
}
+ public Builder(@NonNull CaptureType captureType, int inputFormat) {
+ this(MutableOptionsBundle.create(), captureType);
+ mOptionsBundle.insertOption(OPTION_INPUT_FORMAT, inputFormat);
+ }
+
public Builder(@NonNull Config config, @NonNull CaptureType captureType) {
mOptionsBundle = MutableOptionsBundle.from(config);
setTargetClass(FakeUseCase.class);
diff --git a/camera/camera-testing/src/test/java/androidx/camera/testing/fakes/FakeCameraDeviceSurfaceManagerTest.java b/camera/camera-testing/src/test/java/androidx/camera/testing/fakes/FakeCameraDeviceSurfaceManagerTest.java
index 7bd2de4..0f3fa1c 100644
--- a/camera/camera-testing/src/test/java/androidx/camera/testing/fakes/FakeCameraDeviceSurfaceManagerTest.java
+++ b/camera/camera-testing/src/test/java/androidx/camera/testing/fakes/FakeCameraDeviceSurfaceManagerTest.java
@@ -23,7 +23,6 @@
import static com.google.common.truth.Truth.assertThat;
-import static java.util.Arrays.asList;
import static java.util.Collections.emptyList;
import static java.util.Collections.singletonList;
@@ -31,6 +30,7 @@
import android.util.Range;
import android.util.Size;
+import androidx.annotation.NonNull;
import androidx.camera.core.ImageAnalysis;
import androidx.camera.core.impl.AttachedSurfaceInfo;
import androidx.camera.core.impl.StreamSpec;
@@ -45,6 +45,7 @@
import org.robolectric.annotation.internal.DoNotInstrument;
import java.util.Collections;
+import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -69,8 +70,6 @@
private FakeUseCaseConfig mFakeUseCaseConfig;
- private List<UseCaseConfig<?>> mUseCaseConfigList;
-
@Before
public void setUp() {
mFakeCameraDeviceSurfaceManager = new FakeCameraDeviceSurfaceManager();
@@ -82,8 +81,6 @@
mFakeCameraDeviceSurfaceManager.setSuggestedStreamSpec(FAKE_CAMERA_ID1,
mFakeUseCaseConfig.getClass(),
StreamSpec.builder(new Size(FAKE_WIDTH1, FAKE_HEIGHT1)).build());
-
- mUseCaseConfigList = singletonList(mFakeUseCaseConfig);
}
@Test
@@ -94,7 +91,7 @@
/* isConcurrentCameraModeOn = */false,
FAKE_CAMERA_ID0,
emptyList(),
- asList(preview, analysis));
+ createConfigOutputSizesMap(preview, analysis));
}
@Test(expected = IllegalArgumentException.class)
@@ -109,7 +106,7 @@
mFakeCameraDeviceSurfaceManager.getSuggestedStreamSpecs(
/* isConcurrentCameraModeOn = */false,
FAKE_CAMERA_ID0,
- singletonList(analysis), asList(preview, video));
+ singletonList(analysis), createConfigOutputSizesMap(preview, video));
}
@Test(expected = IllegalArgumentException.class)
@@ -120,7 +117,7 @@
mFakeCameraDeviceSurfaceManager.getSuggestedStreamSpecs(
/* isConcurrentCameraModeOn = */false,
FAKE_CAMERA_ID0,
- Collections.emptyList(), asList(preview, video, analysis));
+ Collections.emptyList(), createConfigOutputSizesMap(preview, video, analysis));
}
@Test
@@ -129,12 +126,12 @@
mFakeCameraDeviceSurfaceManager.getSuggestedStreamSpecs(
/* isConcurrentCameraModeOn = */false,
FAKE_CAMERA_ID0,
- Collections.emptyList(), mUseCaseConfigList);
+ Collections.emptyList(), createConfigOutputSizesMap(mFakeUseCaseConfig));
Map<UseCaseConfig<?>, StreamSpec> suggestedStreamSpecCamera1 =
mFakeCameraDeviceSurfaceManager.getSuggestedStreamSpecs(
/* isConcurrentCameraModeOn = */false,
FAKE_CAMERA_ID1,
- Collections.emptyList(), mUseCaseConfigList);
+ Collections.emptyList(), createConfigOutputSizesMap(mFakeUseCaseConfig));
assertThat(suggestedStreamSpecsCamera0.get(mFakeUseCaseConfig)).isEqualTo(
StreamSpec.builder(new Size(FAKE_WIDTH0, FAKE_HEIGHT0)).build());
@@ -142,4 +139,12 @@
StreamSpec.builder(new Size(FAKE_WIDTH1, FAKE_HEIGHT1)).build());
}
+ private Map<UseCaseConfig<?>, List<Size>> createConfigOutputSizesMap(
+ @NonNull UseCaseConfig<?>... useCaseConfigs) {
+ Map<UseCaseConfig<?>, List<Size>> configOutputSizesMap = new HashMap<>();
+ for (UseCaseConfig<?> useCaseConfig : useCaseConfigs) {
+ configOutputSizesMap.put(useCaseConfig, Collections.emptyList());
+ }
+ return configOutputSizesMap;
+ }
}