Check whether stream use case is supported and invoke populateStreamUseCaseStreamSpecOption() in SupportedSurfaceCombination
Test: SupportedSurfaceCombinationTest
Bug: 266879290
Change-Id: I5406f54fa9065ab37dbe105c077b232586bf5b38
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/GuaranteedConfigurationsUtil.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/GuaranteedConfigurationsUtil.java
index 334952fd..1213e14 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/GuaranteedConfigurationsUtil.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/GuaranteedConfigurationsUtil.java
@@ -689,6 +689,132 @@
}
/**
+ * Returns the entire supported stream combinations for devices with Stream Use Case capability
+ */
+ @NonNull
+ public static List<SurfaceCombination> getStreamUseCaseSupportedCombinationList() {
+ List<SurfaceCombination> combinationList = new ArrayList<>();
+
+ // (PRIV, s1440p)
+ SurfaceCombination surfaceCombination1 = new SurfaceCombination();
+ surfaceCombination1.addSurfaceConfig(
+ SurfaceConfig.create(ConfigType.PRIV, ConfigSize.s1440p));
+ combinationList.add(surfaceCombination1);
+
+ // (YUV, s1440p)
+ SurfaceCombination surfaceCombination2 = new SurfaceCombination();
+ surfaceCombination2.addSurfaceConfig(
+ SurfaceConfig.create(ConfigType.YUV, ConfigSize.s1440p));
+ combinationList.add(surfaceCombination2);
+
+ // (PRIV, RECORD)
+ SurfaceCombination surfaceCombination3 = new SurfaceCombination();
+ surfaceCombination3.addSurfaceConfig(
+ SurfaceConfig.create(ConfigType.PRIV, ConfigSize.RECORD));
+ combinationList.add(surfaceCombination3);
+
+ // (YUV, RECORD)
+ SurfaceCombination surfaceCombination4 = new SurfaceCombination();
+ surfaceCombination4.addSurfaceConfig(
+ SurfaceConfig.create(ConfigType.YUV, ConfigSize.RECORD));
+ combinationList.add(surfaceCombination4);
+
+ // (JPEG, MAXIMUM)
+ SurfaceCombination surfaceCombination5 = new SurfaceCombination();
+ surfaceCombination5.addSurfaceConfig(
+ SurfaceConfig.create(ConfigType.JPEG, ConfigSize.MAXIMUM));
+ combinationList.add(surfaceCombination5);
+
+ // (YUV, MAXIMUM)
+ SurfaceCombination surfaceCombination6 = new SurfaceCombination();
+ surfaceCombination6.addSurfaceConfig(
+ SurfaceConfig.create(ConfigType.YUV, ConfigSize.MAXIMUM));
+ combinationList.add(surfaceCombination6);
+
+ // (PRIV, PREVIEW) + (JPEG, MAXIMUM)
+ SurfaceCombination surfaceCombination7 = new SurfaceCombination();
+ surfaceCombination7.addSurfaceConfig(
+ SurfaceConfig.create(ConfigType.PRIV, ConfigSize.PREVIEW));
+ surfaceCombination7.addSurfaceConfig(
+ SurfaceConfig.create(ConfigType.JPEG, ConfigSize.MAXIMUM));
+ combinationList.add(surfaceCombination7);
+
+ // (PRIV, PREVIEW) + (YUV, MAXIMUM)
+ SurfaceCombination surfaceCombination8 = new SurfaceCombination();
+ surfaceCombination8.addSurfaceConfig(
+ SurfaceConfig.create(ConfigType.PRIV, ConfigSize.PREVIEW));
+ surfaceCombination8.addSurfaceConfig(
+ SurfaceConfig.create(ConfigType.YUV, ConfigSize.MAXIMUM));
+ combinationList.add(surfaceCombination8);
+
+ // (PRIV, PREVIEW) + (PRIV, RECORD)
+ SurfaceCombination surfaceCombination9 = new SurfaceCombination();
+ surfaceCombination9.addSurfaceConfig(
+ SurfaceConfig.create(ConfigType.PRIV, ConfigSize.PREVIEW));
+ surfaceCombination9.addSurfaceConfig(
+ SurfaceConfig.create(ConfigType.PRIV, ConfigSize.RECORD));
+ combinationList.add(surfaceCombination9);
+
+ // (PRIV, PREVIEW) + (YUV, RECORD)
+ SurfaceCombination surfaceCombination10 = new SurfaceCombination();
+ surfaceCombination10.addSurfaceConfig(
+ SurfaceConfig.create(ConfigType.PRIV, ConfigSize.PREVIEW));
+ surfaceCombination10.addSurfaceConfig(
+ SurfaceConfig.create(ConfigType.YUV, ConfigSize.RECORD));
+ combinationList.add(surfaceCombination10);
+
+ // (PRIV, PREVIEW) + (YUV, PREVIEW)
+ SurfaceCombination surfaceCombination11 = new SurfaceCombination();
+ surfaceCombination11.addSurfaceConfig(
+ SurfaceConfig.create(ConfigType.PRIV, ConfigSize.PREVIEW));
+ surfaceCombination11.addSurfaceConfig(
+ SurfaceConfig.create(ConfigType.YUV, ConfigSize.PREVIEW));
+ combinationList.add(surfaceCombination11);
+
+ // (PRIV, PREVIEW) + (PRIV, RECORD) + (JPEG, RECORD)
+ SurfaceCombination surfaceCombination12 = new SurfaceCombination();
+ surfaceCombination12.addSurfaceConfig(
+ SurfaceConfig.create(ConfigType.PRIV, ConfigSize.PREVIEW));
+ surfaceCombination12.addSurfaceConfig(
+ SurfaceConfig.create(ConfigType.PRIV, ConfigSize.RECORD));
+ surfaceCombination12.addSurfaceConfig(
+ SurfaceConfig.create(ConfigType.JPEG, ConfigSize.RECORD));
+ combinationList.add(surfaceCombination12);
+
+ // (PRIV, PREVIEW) + (PRIV, RECORD) + (JPEG, RECORD)
+ SurfaceCombination surfaceCombination13 = new SurfaceCombination();
+ surfaceCombination13.addSurfaceConfig(
+ SurfaceConfig.create(ConfigType.PRIV, ConfigSize.PREVIEW));
+ surfaceCombination13.addSurfaceConfig(
+ SurfaceConfig.create(ConfigType.PRIV, ConfigSize.RECORD));
+ surfaceCombination13.addSurfaceConfig(
+ SurfaceConfig.create(ConfigType.JPEG, ConfigSize.RECORD));
+ combinationList.add(surfaceCombination13);
+
+ // (PRIV, PREVIEW) + (YUV, RECORD) + (JPEG, RECORD)
+ SurfaceCombination surfaceCombination14 = new SurfaceCombination();
+ surfaceCombination14.addSurfaceConfig(
+ SurfaceConfig.create(ConfigType.PRIV, ConfigSize.PREVIEW));
+ surfaceCombination14.addSurfaceConfig(
+ SurfaceConfig.create(ConfigType.YUV, ConfigSize.RECORD));
+ surfaceCombination14.addSurfaceConfig(
+ SurfaceConfig.create(ConfigType.JPEG, ConfigSize.RECORD));
+ combinationList.add(surfaceCombination14);
+
+ // (PRIV, PREVIEW) + (YUV, PREVIEW) + (JPEG, MAXIMUM)
+ SurfaceCombination surfaceCombination15 = new SurfaceCombination();
+ surfaceCombination15.addSurfaceConfig(
+ SurfaceConfig.create(ConfigType.PRIV, ConfigSize.PREVIEW));
+ surfaceCombination15.addSurfaceConfig(
+ SurfaceConfig.create(ConfigType.YUV, ConfigSize.PREVIEW));
+ surfaceCombination15.addSurfaceConfig(
+ SurfaceConfig.create(ConfigType.JPEG, ConfigSize.MAXIMUM));
+ combinationList.add(surfaceCombination14);
+
+ return combinationList;
+ }
+
+ /**
* Returns the supported stream combinations based on the hardware level and capabilities of
* the device.
*/
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/StreamUseCaseUtil.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/StreamUseCaseUtil.java
index 1f1efeb..82bfaf2 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/StreamUseCaseUtil.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/StreamUseCaseUtil.java
@@ -212,6 +212,32 @@
}
/**
+ * Return true if the given camera characteristics support stream use case
+ */
+ @OptIn(markerClass = ExperimentalCamera2Interop.class)
+ public static boolean isStreamUseCaseSupported(
+ @NonNull CameraCharacteristicsCompat characteristicsCompat) {
+ if (Build.VERSION.SDK_INT < 33) {
+ return false;
+ }
+ long[] availableStreamUseCases = characteristicsCompat.get(
+ CameraCharacteristics.SCALER_AVAILABLE_STREAM_USE_CASES);
+ if (availableStreamUseCases == null || availableStreamUseCases.length == 0) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Return true if the given feature settings is appropriate for stream use case usage.
+ */
+ public static boolean shouldUseStreamUseCase(@NonNull
+ SupportedSurfaceCombination.FeatureSettings featureSettings) {
+ return featureSettings.getCameraMode() == CameraMode.DEFAULT
+ && featureSettings.getRequiredMaxBitDepth() == DynamicRange.BIT_DEPTH_8_BIT;
+ }
+
+ /**
* Populate the {@link STREAM_USE_CASE_STREAM_SPEC_OPTION} option in StreamSpecs for both
* existing UseCases and new UseCases to be attached. This option will be written into the
* session configurations of the UseCases. When creating a new capture session during
@@ -219,30 +245,20 @@
* {@link android.hardware.camera2.params.OutputConfiguration#setStreamUseCase(long)}
*
* @param characteristicsCompat the camera characteristics of the device
- * @param featureSettings the feature settings of this capture
* @param attachedSurfaces surface info of the already attached use cases
* @param suggestedStreamSpecMap the UseCaseConfig-to-StreamSpec map for new use cases
* @param attachedSurfaceStreamSpecMap the SurfaceInfo-to-StreamSpec map for attached use cases
* whose StreamSpecs needs to be updated
+ * @return true if StreamSpec options are populated. False if not.
*/
@OptIn(markerClass = ExperimentalCamera2Interop.class)
- public static void populateStreamUseCaseStreamSpecOption(
+ public static boolean populateStreamUseCaseStreamSpecOptionWithInteropOverride(
@NonNull CameraCharacteristicsCompat characteristicsCompat,
- @NonNull SupportedSurfaceCombination.FeatureSettings featureSettings,
@NonNull List<AttachedSurfaceInfo> attachedSurfaces,
@NonNull Map<UseCaseConfig<?>, StreamSpec> suggestedStreamSpecMap,
@NonNull Map<AttachedSurfaceInfo, StreamSpec> attachedSurfaceStreamSpecMap) {
if (Build.VERSION.SDK_INT < 33) {
- return;
- }
- long[] availableStreamUseCases = characteristicsCompat.get(
- CameraCharacteristics.SCALER_AVAILABLE_STREAM_USE_CASES);
- if (availableStreamUseCases == null || availableStreamUseCases.length == 0) {
- return;
- }
- if (featureSettings.getCameraMode() != CameraMode.DEFAULT
- || featureSettings.getRequiredMaxBitDepth() != DynamicRange.BIT_DEPTH_8_BIT) {
- return;
+ return false;
}
List<UseCaseConfig<?>> newUseCaseConfigs = new ArrayList<>(suggestedStreamSpecMap.keySet());
// All AttachedSurfaceInfo should have implementation options
@@ -254,10 +270,9 @@
Preconditions.checkNotNull(Preconditions.checkNotNull(
suggestedStreamSpecMap.get(useCaseConfig)).getImplementationOptions());
}
- if (containsZslUseCase(attachedSurfaces, newUseCaseConfigs)) {
- return;
- }
Set<Long> availableStreamUseCaseSet = new HashSet<>();
+ long[] availableStreamUseCases = characteristicsCompat.get(
+ CameraCharacteristics.SCALER_AVAILABLE_STREAM_USE_CASES);
for (Long availableStreamUseCase : availableStreamUseCases) {
availableStreamUseCaseSet.add(availableStreamUseCase);
}
@@ -297,7 +312,9 @@
suggestedStreamSpecMap.put(newUseCaseConfig, newStreamSpec);
}
}
+ return true;
}
+ return false;
}
/**
@@ -324,9 +341,9 @@
/**
* Return true if any one of the existing or new UseCases is ZSL.
*/
- private static boolean containsZslUseCase(
- List<AttachedSurfaceInfo> attachedSurfaces,
- List<UseCaseConfig<?>> newUseCaseConfigs) {
+ public static boolean containsZslUseCase(
+ @NonNull List<AttachedSurfaceInfo> attachedSurfaces,
+ @NonNull List<UseCaseConfig<?>> newUseCaseConfigs) {
for (AttachedSurfaceInfo attachedSurfaceInfo : attachedSurfaces) {
List<UseCaseConfigFactory.CaptureType> captureTypes =
attachedSurfaceInfo.getCaptureTypes();
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 b9a91cf..bb83079 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
@@ -41,6 +41,7 @@
import androidx.annotation.DoNotInline;
import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.annotation.OptIn;
import androidx.annotation.RequiresApi;
import androidx.annotation.VisibleForTesting;
@@ -87,6 +88,7 @@
* devices. This structure is used to store a list of surface combinations that are guaranteed to
* support for this camera device.
*/
+@OptIn(markerClass = ExperimentalCamera2Interop.class)
@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
final class SupportedSurfaceCombination {
private static final String TAG = "SupportedSurfaceCombination";
@@ -96,6 +98,7 @@
private final Map<FeatureSettings, List<SurfaceCombination>>
mFeatureSettingsToSupportedCombinationsMap = new HashMap<>();
private final List<SurfaceCombination> mSurfaceCombinations10Bit = new ArrayList<>();
+ private final List<SurfaceCombination> mSurfaceCombinationsStreamUseCase = new ArrayList<>();
private final String mCameraId;
private final CamcorderProfileHelper mCamcorderProfileHelper;
private final CameraCharacteristicsCompat mCharacteristics;
@@ -105,6 +108,7 @@
private boolean mIsRawSupported = false;
private boolean mIsBurstCaptureSupported = false;
private boolean mIsConcurrentCameraModeSupported = false;
+ private boolean mIsStreamUseCaseSupported = false;
private boolean mIsUltraHighResolutionSensorSupported = false;
@VisibleForTesting
SurfaceSizeDefinition mSurfaceSizeDefinition;
@@ -175,6 +179,11 @@
generate10BitSupportedCombinationList();
}
+ mIsStreamUseCaseSupported = StreamUseCaseUtil.isStreamUseCaseSupported(mCharacteristics);
+ if (mIsStreamUseCaseSupported) {
+ generateStreamUseCaseSupportedCombinationList();
+ }
+
generateSurfaceSizeDefinition();
checkCustomization();
}
@@ -217,6 +226,25 @@
return isSupported;
}
+ @Nullable
+ SurfaceCombination getSupportedStreamUseCaseSurfaceCombination(
+ @NonNull FeatureSettings featureSettings,
+ List<SurfaceConfig> surfaceConfigList) {
+ if (!StreamUseCaseUtil.shouldUseStreamUseCase(featureSettings)) {
+ return null;
+ }
+
+ for (SurfaceCombination surfaceCombination : mSurfaceCombinationsStreamUseCase) {
+ // Stream use case table doesn't support sublist. SurfaceCombination and
+ // SurfaceConfig list need to be EXACT match.
+ if (surfaceCombination.getSurfaceConfigList().size() == surfaceConfigList.size()
+ && surfaceCombination.isSupported(surfaceConfigList)) {
+ return surfaceCombination;
+ }
+ }
+ return null;
+ }
+
/**
* Returns the supported surface combinations according to the specified feature
* settings.
@@ -503,7 +531,6 @@
* of {@link DynamicRange}, or requiring an
* unsupported combination of camera features.
*/
- @OptIn(markerClass = ExperimentalCamera2Interop.class)
@NonNull
Pair<Map<UseCaseConfig<?>, StreamSpec>, Map<AttachedSurfaceInfo, StreamSpec>>
getSuggestedStreamSpecifications(
@@ -547,7 +574,16 @@
getUpdatedSurfaceSizeDefinitionByFormat(imageFormat)));
}
- if (!checkSupported(featureSettings, surfaceConfigs)) {
+ boolean containsZsl = StreamUseCaseUtil.containsZslUseCase(attachedSurfaces,
+ newUseCaseConfigs);
+ SurfaceCombination surfaceCombinationForStreamUseCase =
+ mIsStreamUseCaseSupported && !containsZsl
+ ? getSupportedStreamUseCaseSurfaceCombination(featureSettings,
+ surfaceConfigs) : null;
+
+ boolean isSurfaceCombinationSupported = checkSupported(featureSettings, surfaceConfigs);
+
+ if (surfaceCombinationForStreamUseCase == null && !isSurfaceCombinationSupported) {
throw new IllegalArgumentException(
"No supported surface combination is found for camera device - Id : "
+ mCameraId + ". May be attempting to bind too many use cases. "
@@ -593,42 +629,53 @@
targetFramerateForConfig);
}
- Map<UseCaseConfig<?>, StreamSpec> suggestedStreamSpecMap;
+ Map<AttachedSurfaceInfo, StreamSpec> attachedSurfaceStreamSpecMap = new HashMap<>();
+ Map<UseCaseConfig<?>, StreamSpec> suggestedStreamSpecMap = new HashMap<>();
+
List<Size> savedSizes = null;
int savedConfigMaxFps = Integer.MAX_VALUE;
+ List<Size> savedSizesForStreamUseCase = null;
+ int savedConfigMaxFpsForStreamUseCase = Integer.MAX_VALUE;
+
+ if (surfaceCombinationForStreamUseCase != null) {
+ // Check if any possible size arrangement is supported for stream use case.
+ for (List<Size> possibleSizeList : allPossibleSizeArrangements) {
+ List<SurfaceConfig> surfaceConfigList = getSurfaceConfigListAndFpsCeiling(
+ cameraMode,
+ attachedSurfaces, possibleSizeList, newUseCaseConfigs,
+ useCasesPriorityOrder, existingSurfaceFrameRateCeiling).first;
+ surfaceCombinationForStreamUseCase =
+ getSupportedStreamUseCaseSurfaceCombination(featureSettings,
+ surfaceConfigList);
+ if (surfaceCombinationForStreamUseCase != null) {
+ break;
+ }
+ }
+
+ // We can terminate early if surface combination is not supported and none of the
+ // possible size arrangement supports stream use case either.
+ if (surfaceCombinationForStreamUseCase == null && !isSurfaceCombinationSupported) {
+ throw new IllegalArgumentException(
+ "No supported surface combination is found for camera device - Id : "
+ + mCameraId + ". May be attempting to bind too many use cases. "
+ + "Existing surfaces: " + attachedSurfaces + " New configs: "
+ + newUseCaseConfigs);
+ }
+ }
+
+ boolean supportedSizesFound = false;
+ boolean supportedSizesForStreamUseCaseFound = false;
// Transform use cases to SurfaceConfig list and find the first (best) workable combination
for (List<Size> possibleSizeList : allPossibleSizeArrangements) {
// Attach SurfaceConfig of original use cases since it will impact the new use cases
- List<SurfaceConfig> surfaceConfigList = new ArrayList<>();
- int currentConfigFramerateCeiling = existingSurfaceFrameRateCeiling;
+ Pair<List<SurfaceConfig>, Integer> resultPair =
+ getSurfaceConfigListAndFpsCeiling(cameraMode,
+ attachedSurfaces, possibleSizeList, newUseCaseConfigs,
+ useCasesPriorityOrder, existingSurfaceFrameRateCeiling);
+ List<SurfaceConfig> surfaceConfigList = resultPair.first;
+ int currentConfigFramerateCeiling = resultPair.second;
boolean isConfigFrameRateAcceptable = true;
-
- for (AttachedSurfaceInfo attachedSurfaceInfo : attachedSurfaces) {
- surfaceConfigList.add(attachedSurfaceInfo.getSurfaceConfig());
- }
-
- // Attach SurfaceConfig of new use cases
- for (int i = 0; i < possibleSizeList.size(); i++) {
- Size size = possibleSizeList.get(i);
- UseCaseConfig<?> newUseCase =
- newUseCaseConfigs.get(useCasesPriorityOrder.get(i));
- int imageFormat = newUseCase.getInputFormat();
- // add new use case/size config to list of surfaces
- surfaceConfigList.add(
- SurfaceConfig.transformSurfaceConfig(
- cameraMode,
- imageFormat,
- size,
- getUpdatedSurfaceSizeDefinitionByFormat(imageFormat)));
-
- // get the maximum fps of the new surface and update the maximum fps of the
- // proposed configuration
- currentConfigFramerateCeiling = getUpdatedMaximumFps(
- currentConfigFramerateCeiling,
- newUseCase.getInputFormat(),
- size);
- }
if (targetFramerateForConfig != null) {
if (existingSurfaceFrameRateCeiling > currentConfigFramerateCeiling
&& currentConfigFramerateCeiling < targetFramerateForConfig.getLower()) {
@@ -640,8 +687,11 @@
}
}
+ // Find the same possible size arrangement that is supported by stream use case again
+ // if we found one earlier.
+
// only change the saved config if you get another that has a better max fps
- if (checkSupported(featureSettings, surfaceConfigList)) {
+ if (!supportedSizesFound && checkSupported(featureSettings, surfaceConfigList)) {
// if the config is supported by the device but doesn't meet the target framerate,
// save the config
if (savedConfigMaxFps == Integer.MAX_VALUE) {
@@ -657,7 +707,34 @@
if (isConfigFrameRateAcceptable) {
savedConfigMaxFps = currentConfigFramerateCeiling;
savedSizes = possibleSizeList;
- break;
+ supportedSizesFound = true;
+ if (supportedSizesForStreamUseCaseFound) {
+ break;
+ }
+ }
+ }
+ // If we already know that there is a supported surface combination from the stream
+ // use case table, keep an independent tracking on the saved sizes and max FPS. Only
+ // use stream use case if the save sizes for the normal case and for stream use case
+ // are the same.
+ if (surfaceCombinationForStreamUseCase != null && !supportedSizesForStreamUseCaseFound
+ && getSupportedStreamUseCaseSurfaceCombination(
+ featureSettings, surfaceConfigList) != null) {
+ if (savedConfigMaxFpsForStreamUseCase == Integer.MAX_VALUE) {
+ savedConfigMaxFpsForStreamUseCase = currentConfigFramerateCeiling;
+ savedSizesForStreamUseCase = possibleSizeList;
+ } else if (savedConfigMaxFpsForStreamUseCase < currentConfigFramerateCeiling) {
+ savedConfigMaxFpsForStreamUseCase = currentConfigFramerateCeiling;
+ savedSizesForStreamUseCase = possibleSizeList;
+ }
+
+ if (isConfigFrameRateAcceptable) {
+ savedConfigMaxFpsForStreamUseCase = currentConfigFramerateCeiling;
+ savedSizesForStreamUseCase = possibleSizeList;
+ supportedSizesForStreamUseCaseFound = true;
+ if (supportedSizesFound) {
+ break;
+ }
}
}
}
@@ -670,7 +747,6 @@
getClosestSupportedDeviceFrameRate(targetFramerateForConfig,
savedConfigMaxFps);
}
- suggestedStreamSpecMap = new HashMap<>();
for (UseCaseConfig<?> useCaseConfig : newUseCaseConfigs) {
Size resolutionForUseCase = savedSizes.get(
useCasesPriorityOrder.indexOf(newUseCaseConfigs.indexOf(useCaseConfig)));
@@ -693,11 +769,67 @@
+ " Existing surfaces: " + attachedSurfaces
+ " New configs: " + newUseCaseConfigs);
}
- Map<AttachedSurfaceInfo, StreamSpec> attachedSurfaceStreamSpecMap = new HashMap<>();
- // TODO(b/266879290): Invoke StreamUSeCaseUtil.populateStreamUseCaseStreamSpecOption()
+
+ // Only perform stream use case operations if the saved max FPS and sizes are the same
+ if (surfaceCombinationForStreamUseCase != null
+ && savedConfigMaxFps == savedConfigMaxFpsForStreamUseCase
+ && savedSizes.size() == savedSizesForStreamUseCase.size()) {
+ boolean hasDifferenceSavedSizes = false;
+ for (int i = 0; i < savedSizes.size(); i++) {
+ if (!savedSizes.get(i).equals(savedSizesForStreamUseCase.get(i))) {
+ hasDifferenceSavedSizes = true;
+ break;
+ }
+ }
+ if (!hasDifferenceSavedSizes) {
+ boolean hasStreamUseCaseOverride =
+ StreamUseCaseUtil.populateStreamUseCaseStreamSpecOptionWithInteropOverride(
+ mCharacteristics, attachedSurfaces, suggestedStreamSpecMap,
+ attachedSurfaceStreamSpecMap);
+ if (!hasStreamUseCaseOverride) {
+ // TODO(b/280335430): Use surfaceCombinationForStreamUseCase to populate the
+ // streamSpec maps
+ }
+ }
+ }
+
return new Pair<>(suggestedStreamSpecMap, attachedSurfaceStreamSpecMap);
}
+ private Pair<List<SurfaceConfig>, Integer> getSurfaceConfigListAndFpsCeiling(
+ @CameraMode.Mode int cameraMode,
+ List<AttachedSurfaceInfo> attachedSurfaces,
+ List<Size> possibleSizeList, List<UseCaseConfig<?>> newUseCaseConfigs,
+ List<Integer> useCasesPriorityOrder,
+ int currentConfigFramerateCeiling) {
+ List<SurfaceConfig> surfaceConfigList = new ArrayList<>();
+ for (AttachedSurfaceInfo attachedSurfaceInfo : attachedSurfaces) {
+ surfaceConfigList.add(attachedSurfaceInfo.getSurfaceConfig());
+ }
+
+ // Attach SurfaceConfig of new use cases
+ for (int i = 0; i < possibleSizeList.size(); i++) {
+ Size size = possibleSizeList.get(i);
+ UseCaseConfig<?> newUseCase =
+ newUseCaseConfigs.get(useCasesPriorityOrder.get(i));
+ int imageFormat = newUseCase.getInputFormat();
+ // add new use case/size config to list of surfaces
+ surfaceConfigList.add(
+ SurfaceConfig.transformSurfaceConfig(
+ cameraMode,
+ imageFormat,
+ size,
+ getUpdatedSurfaceSizeDefinitionByFormat(imageFormat)));
+ // get the maximum fps of the new surface and update the maximum fps of the
+ // proposed configuration
+ currentConfigFramerateCeiling = getUpdatedMaximumFps(
+ currentConfigFramerateCeiling,
+ newUseCase.getInputFormat(),
+ size);
+ }
+ return new Pair<>(surfaceConfigList, currentConfigFramerateCeiling);
+ }
+
/**
* Applies resolution selection order related workarounds.
*
@@ -925,6 +1057,11 @@
GuaranteedConfigurationsUtil.get10BitSupportedCombinationList());
}
+ private void generateStreamUseCaseSupportedCombinationList() {
+ mSurfaceCombinationsStreamUseCase.addAll(
+ GuaranteedConfigurationsUtil.getStreamUseCaseSupportedCombinationList());
+ }
+
private void checkCustomization() {
// TODO(b/119466260): Integrate found feasible stream combinations into supported list
}
diff --git a/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/StreamUseCaseTest.java b/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/StreamUseCaseTest.java
index 3919413..7439f09 100644
--- a/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/StreamUseCaseTest.java
+++ b/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/StreamUseCaseTest.java
@@ -323,122 +323,63 @@
@SdkSuppress(minSdkVersion = 33)
@Test
- public void populateStreamUseCaseStreamSpecOption_streamUseCaseNotAvailable() {
- Map<UseCaseConfig<?>, StreamSpec> suggestedStreamSpecMap = new HashMap<>();
- UseCaseConfig<?> useCaseConfig = getFakeUseCaseConfigWithOptions(true, false, false,
- UseCaseConfigFactory.CaptureType.PREVIEW, ImageFormat.PRIVATE);
- suggestedStreamSpecMap.put(useCaseConfig,
- getFakeStreamSpecFromFakeUseCaseConfig(useCaseConfig));
- StreamUseCaseUtil.populateStreamUseCaseStreamSpecOption(
- getCameraCharacteristicsCompat(true),
- SupportedSurfaceCombination.FeatureSettings.of(CameraMode.DEFAULT,
- DynamicRange.BIT_DEPTH_8_BIT), new ArrayList<>(), suggestedStreamSpecMap,
- new HashMap<>());
- assertFalse(suggestedStreamSpecMap.get(useCaseConfig)
- .getImplementationOptions().containsOption(
- STREAM_USE_CASE_STREAM_SPEC_OPTION));
+ public void isStreamUseCaseSupported_streamUseCaseNotAvailable() {
+ assertFalse(StreamUseCaseUtil.isStreamUseCaseSupported(
+ getCameraCharacteristicsCompat(true)));
}
@SdkSuppress(minSdkVersion = 33)
@Test
- public void populateStreamUseCaseStreamSpecOption_cameraModeNotSupported() {
- Map<UseCaseConfig<?>, StreamSpec> suggestedStreamSpecMap = new HashMap<>();
- UseCaseConfig<?> useCaseConfig = getFakeUseCaseConfigWithOptions(true, false, false,
- UseCaseConfigFactory.CaptureType.PREVIEW, ImageFormat.PRIVATE);
- suggestedStreamSpecMap.put(useCaseConfig,
- getFakeStreamSpecFromFakeUseCaseConfig(useCaseConfig));
- StreamUseCaseUtil.populateStreamUseCaseStreamSpecOption(
- getCameraCharacteristicsCompat(),
+ public void shouldUseStreamUseCase_cameraModeNotSupported() {
+ assertFalse(StreamUseCaseUtil.shouldUseStreamUseCase(
SupportedSurfaceCombination.FeatureSettings.of(CameraMode.CONCURRENT_CAMERA,
- DynamicRange.BIT_DEPTH_8_BIT), new ArrayList<>(), suggestedStreamSpecMap,
- new HashMap<>());
- assertFalse(suggestedStreamSpecMap.get(useCaseConfig)
- .getImplementationOptions().containsOption(
- STREAM_USE_CASE_STREAM_SPEC_OPTION));
+ DynamicRange.BIT_DEPTH_8_BIT)));
}
@SdkSuppress(minSdkVersion = 33)
@Test
- public void populateStreamUseCaseStreamSpecOption_bitDepthNotSupported() {
- Map<UseCaseConfig<?>, StreamSpec> suggestedStreamSpecMap = new HashMap<>();
- UseCaseConfig<?> useCaseConfig = getFakeUseCaseConfigWithOptions(true, false, false,
- UseCaseConfigFactory.CaptureType.PREVIEW, ImageFormat.PRIVATE);
- suggestedStreamSpecMap.put(useCaseConfig,
- getFakeStreamSpecFromFakeUseCaseConfig(useCaseConfig));
- StreamUseCaseUtil.populateStreamUseCaseStreamSpecOption(
- getCameraCharacteristicsCompat(),
+ public void shouldUseStreamUseCase_bitDepthNotSupported() {
+ assertFalse(StreamUseCaseUtil.shouldUseStreamUseCase(
SupportedSurfaceCombination.FeatureSettings.of(CameraMode.DEFAULT,
- DynamicRange.BIT_DEPTH_10_BIT), new ArrayList<>(), suggestedStreamSpecMap,
- new HashMap<>());
- assertFalse(suggestedStreamSpecMap.get(useCaseConfig)
- .getImplementationOptions().containsOption(
- STREAM_USE_CASE_STREAM_SPEC_OPTION));
+ DynamicRange.BIT_DEPTH_10_BIT)));
}
@SdkSuppress(minSdkVersion = 33)
@Test
- public void populateStreamUseCaseStreamSpecOption_isZslUseCase() {
- Map<UseCaseConfig<?>, StreamSpec> suggestedStreamSpecMap = new HashMap<>();
+ public void containsZslUseCase_isZslUseCase() {
UseCaseConfig<?> useCaseConfig = getFakeUseCaseConfigWithOptions(true, false, true,
UseCaseConfigFactory.CaptureType.IMAGE_CAPTURE, ImageFormat.JPEG);
- suggestedStreamSpecMap.put(useCaseConfig,
- getFakeStreamSpecFromFakeUseCaseConfig(useCaseConfig));
- StreamUseCaseUtil.populateStreamUseCaseStreamSpecOption(getCameraCharacteristicsCompat(),
- SupportedSurfaceCombination.FeatureSettings.of(CameraMode.DEFAULT,
- DynamicRange.BIT_DEPTH_8_BIT), new ArrayList<>(), suggestedStreamSpecMap,
- new HashMap<>());
- assertFalse(suggestedStreamSpecMap.get(useCaseConfig)
- .getImplementationOptions().containsOption(
- STREAM_USE_CASE_STREAM_SPEC_OPTION));
+ List<UseCaseConfig<?>> useCaseConfigList = new ArrayList<>();
+ useCaseConfigList.add(useCaseConfig);
+ assertTrue(StreamUseCaseUtil.containsZslUseCase(new ArrayList<>(), useCaseConfigList));
}
@SdkSuppress(minSdkVersion = 33)
@Test
- public void populateStreamUseCaseStreamSpecOption_isZslUseCase_ZslDisabled() {
- Map<UseCaseConfig<?>, StreamSpec> suggestedStreamSpecMap = new HashMap<>();
+ public void containsZslUseCase_isZslUseCase_ZslDisabled() {
UseCaseConfig<?> useCaseConfig = getFakeUseCaseConfigWithOptions(true, true, true,
UseCaseConfigFactory.CaptureType.IMAGE_CAPTURE, ImageFormat.JPEG);
- suggestedStreamSpecMap.put(useCaseConfig,
- getFakeStreamSpecFromFakeUseCaseConfig(useCaseConfig));
- StreamUseCaseUtil.populateStreamUseCaseStreamSpecOption(getCameraCharacteristicsCompat(),
- SupportedSurfaceCombination.FeatureSettings.of(CameraMode.DEFAULT,
- DynamicRange.BIT_DEPTH_8_BIT), new ArrayList<>(), suggestedStreamSpecMap,
- new HashMap<>());
- assertTrue(suggestedStreamSpecMap.get(
- useCaseConfig).getImplementationOptions().retrieveOption(
- STREAM_USE_CASE_STREAM_SPEC_OPTION) == TEST_STREAM_USE_CASE_OPTION_VALUE);
+ List<UseCaseConfig<?>> useCaseConfigList = new ArrayList<>();
+ useCaseConfigList.add(useCaseConfig);
+ assertFalse(StreamUseCaseUtil.containsZslUseCase(new ArrayList<>(), useCaseConfigList));
}
@SdkSuppress(minSdkVersion = 33)
@Test
- public void populateStreamUseCaseStreamSpecOption_isZslSurface() {
+ public void containsZslUseCase_isZslSurface() {
List<AttachedSurfaceInfo> attachedSurfaces = new ArrayList<>();
attachedSurfaces.add(getFakeAttachedSurfaceInfo(true, false, true,
UseCaseConfigFactory.CaptureType.IMAGE_CAPTURE, ImageFormat.JPEG));
- Map<AttachedSurfaceInfo, StreamSpec> attachedSurfaceStreamSpecMap = new HashMap<>();
- StreamUseCaseUtil.populateStreamUseCaseStreamSpecOption(getCameraCharacteristicsCompat(),
- SupportedSurfaceCombination.FeatureSettings.of(CameraMode.DEFAULT,
- DynamicRange.BIT_DEPTH_8_BIT), attachedSurfaces, new HashMap<>(),
- attachedSurfaceStreamSpecMap
- );
- assertFalse(attachedSurfaceStreamSpecMap.containsKey(attachedSurfaces.get(0)));
+ assertTrue(StreamUseCaseUtil.containsZslUseCase(attachedSurfaces, new ArrayList<>()));
}
@SdkSuppress(minSdkVersion = 33)
@Test
- public void populateStreamUseCaseStreamSpecOption_isZslSurface_ZslDisabled() {
+ public void containsZslUseCase_isZslSurface_ZslDisabled() {
List<AttachedSurfaceInfo> attachedSurfaces = new ArrayList<>();
attachedSurfaces.add(getFakeAttachedSurfaceInfo(true, true, true,
UseCaseConfigFactory.CaptureType.IMAGE_CAPTURE, ImageFormat.JPEG));
- Map<AttachedSurfaceInfo, StreamSpec> attachedSurfaceStreamSpecMap = new HashMap<>();
- StreamUseCaseUtil.populateStreamUseCaseStreamSpecOption(getCameraCharacteristicsCompat(),
- SupportedSurfaceCombination.FeatureSettings.of(CameraMode.DEFAULT,
- DynamicRange.BIT_DEPTH_8_BIT), attachedSurfaces, new HashMap<>(),
- attachedSurfaceStreamSpecMap
- );
- assertTrue(attachedSurfaceStreamSpecMap.get(
- attachedSurfaces.get(0)).getImplementationOptions().retrieveOption(
- STREAM_USE_CASE_STREAM_SPEC_OPTION) == TEST_STREAM_USE_CASE_OPTION_VALUE);
+ assertFalse(StreamUseCaseUtil.containsZslUseCase(attachedSurfaces, new ArrayList<>()));
}
@SdkSuppress(minSdkVersion = 33)
@@ -449,9 +390,8 @@
UseCaseConfigFactory.CaptureType.PREVIEW, ImageFormat.PRIVATE);
suggestedStreamSpecMap.put(useCaseConfig,
getFakeStreamSpecFromFakeUseCaseConfig(useCaseConfig));
- StreamUseCaseUtil.populateStreamUseCaseStreamSpecOption(getCameraCharacteristicsCompat(),
- SupportedSurfaceCombination.FeatureSettings.of(CameraMode.DEFAULT,
- DynamicRange.BIT_DEPTH_8_BIT), new ArrayList<>(), suggestedStreamSpecMap,
+ StreamUseCaseUtil.populateStreamUseCaseStreamSpecOptionWithInteropOverride(
+ getCameraCharacteristicsCompat(), new ArrayList<>(), suggestedStreamSpecMap,
new HashMap<>());
assertTrue(suggestedStreamSpecMap.get(
useCaseConfig).getImplementationOptions().retrieveOption(
@@ -465,9 +405,8 @@
attachedSurfaces.add(getFakeAttachedSurfaceInfo(true, false, false,
UseCaseConfigFactory.CaptureType.PREVIEW, ImageFormat.PRIVATE));
Map<AttachedSurfaceInfo, StreamSpec> attachedSurfaceStreamSpecMap = new HashMap<>();
- StreamUseCaseUtil.populateStreamUseCaseStreamSpecOption(getCameraCharacteristicsCompat(),
- SupportedSurfaceCombination.FeatureSettings.of(CameraMode.DEFAULT,
- DynamicRange.BIT_DEPTH_8_BIT), attachedSurfaces, new HashMap<>(),
+ StreamUseCaseUtil.populateStreamUseCaseStreamSpecOptionWithInteropOverride(
+ getCameraCharacteristicsCompat(), attachedSurfaces, new HashMap<>(),
attachedSurfaceStreamSpecMap
);
assertTrue(attachedSurfaceStreamSpecMap.get(
@@ -487,9 +426,8 @@
attachedSurfaces.add(getFakeAttachedSurfaceInfo(true, false, false,
UseCaseConfigFactory.CaptureType.PREVIEW, ImageFormat.PRIVATE));
Map<AttachedSurfaceInfo, StreamSpec> attachedSurfaceStreamSpecMap = new HashMap<>();
- StreamUseCaseUtil.populateStreamUseCaseStreamSpecOption(getCameraCharacteristicsCompat(),
- SupportedSurfaceCombination.FeatureSettings.of(CameraMode.DEFAULT,
- DynamicRange.BIT_DEPTH_8_BIT), attachedSurfaces, suggestedStreamSpecMap,
+ StreamUseCaseUtil.populateStreamUseCaseStreamSpecOptionWithInteropOverride(
+ getCameraCharacteristicsCompat(), attachedSurfaces, suggestedStreamSpecMap,
attachedSurfaceStreamSpecMap
);
assertTrue(suggestedStreamSpecMap.get(
@@ -511,9 +449,8 @@
attachedSurfaces.add(getFakeAttachedSurfaceInfo(true, false, false,
UseCaseConfigFactory.CaptureType.PREVIEW, ImageFormat.PRIVATE));
Map<AttachedSurfaceInfo, StreamSpec> attachedSurfaceStreamSpecMap = new HashMap<>();
- StreamUseCaseUtil.populateStreamUseCaseStreamSpecOption(getCameraCharacteristicsCompat(),
- SupportedSurfaceCombination.FeatureSettings.of(CameraMode.DEFAULT,
- DynamicRange.BIT_DEPTH_8_BIT), attachedSurfaces, suggestedStreamSpecMap,
+ StreamUseCaseUtil.populateStreamUseCaseStreamSpecOptionWithInteropOverride(
+ getCameraCharacteristicsCompat(), attachedSurfaces, suggestedStreamSpecMap,
attachedSurfaceStreamSpecMap
);
}
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 d0ea2aa..f847397 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
@@ -38,10 +38,12 @@
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.Size
import android.view.WindowManager
import androidx.camera.camera2.Camera2Config
+import androidx.camera.camera2.impl.Camera2ImplConfig
import androidx.camera.camera2.internal.SupportedSurfaceCombination.FeatureSettings
import androidx.camera.camera2.internal.compat.CameraManagerCompat
import androidx.camera.core.CameraSelector.LensFacing
@@ -109,6 +111,7 @@
private const val DEFAULT_CAMERA_ID = "0"
private const val EXTERNAL_CAMERA_ID = "0-external"
private const val SENSOR_ORIENTATION_90 = 90
+private const val STREAM_USE_CASE_OVERRIDE = 3L
private val LANDSCAPE_PIXEL_ARRAY_SIZE = Size(4032, 3024)
private val DISPLAY_SIZE = Size(720, 1280)
private val PREVIEW_SIZE = Size(1280, 720)
@@ -1539,13 +1542,13 @@
cameraMode: Int = CameraMode.DEFAULT,
dynamicRangeProfiles: DynamicRangeProfiles? = null,
default10BitProfile: Long? = null,
- useCasesExpectedDynamicRangeMap: Map<UseCase, DynamicRange> = emptyMap()
- ) {
+ useCasesExpectedDynamicRangeMap: Map<UseCase, DynamicRange> = emptyMap(),
+ ): Pair<Map<UseCaseConfig<*>, StreamSpec>, Map<AttachedSurfaceInfo, StreamSpec>> {
setupCameraAndInitCameraX(
hardwareLevel = hardwareLevel,
capabilities = capabilities,
dynamicRangeProfiles = dynamicRangeProfiles,
- default10BitProfile = default10BitProfile
+ default10BitProfile = default10BitProfile,
)
val supportedSurfaceCombination = SupportedSurfaceCombination(
context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
@@ -1554,14 +1557,17 @@
val useCaseConfigMap = getUseCaseToConfigMap(useCasesExpectedSizeMap.keys.toList())
val useCaseConfigToOutputSizesMap =
getUseCaseConfigToOutputSizesMap(useCaseConfigMap.values.toList())
- val suggestedStreamSpecs = supportedSurfaceCombination.getSuggestedStreamSpecifications(
+ val resultPair = supportedSurfaceCombination.getSuggestedStreamSpecifications(
cameraMode,
attachedSurfaceInfoList,
useCaseConfigToOutputSizesMap
- ).first
+ )
+ val suggestedStreamSpecsForNewUseCases = resultPair.first
+ val suggestedStreamSpecsForOldSurfaces = resultPair.second
+ var hasStreamUseCaseStreamSpecOption: Boolean? = null
useCasesExpectedSizeMap.keys.forEach {
- val resultSize = suggestedStreamSpecs[useCaseConfigMap[it]]!!.resolution
+ val resultSize = suggestedStreamSpecsForNewUseCases[useCaseConfigMap[it]]!!.resolution
val expectedSize = useCasesExpectedSizeMap[it]!!
if (!compareWithAtMost) {
assertThat(resultSize).isEqualTo(expectedSize)
@@ -1571,18 +1577,59 @@
if (compareExpectedFps != null) {
assertThat(
- suggestedStreamSpecs[useCaseConfigMap[it]]!!.expectedFrameRateRange
- == compareExpectedFps
+ suggestedStreamSpecsForNewUseCases[useCaseConfigMap[it]]!!
+ .expectedFrameRateRange == compareExpectedFps
)
}
}
useCasesExpectedDynamicRangeMap.keys.forEach {
- val resultDynamicRange = suggestedStreamSpecs[useCaseConfigMap[it]]!!.dynamicRange
+ val resultDynamicRange =
+ suggestedStreamSpecsForNewUseCases[useCaseConfigMap[it]]!!.dynamicRange
val expectedDynamicRange = useCasesExpectedDynamicRangeMap[it]
assertThat(resultDynamicRange).isEqualTo(expectedDynamicRange)
}
+
+ // Assert that if one stream specification has stream use case options, all other
+ // stream specifications also have it.
+ suggestedStreamSpecsForNewUseCases.entries.forEach {
+ if (it.value.implementationOptions?.containsOption(
+ StreamUseCaseUtil.STREAM_USE_CASE_STREAM_SPEC_OPTION
+ ) == true
+ ) {
+ if (hasStreamUseCaseStreamSpecOption != null) {
+ assertThat(hasStreamUseCaseStreamSpecOption).isTrue()
+ } else {
+ hasStreamUseCaseStreamSpecOption = true
+ }
+ } else {
+ if (hasStreamUseCaseStreamSpecOption != null) {
+ assertThat(hasStreamUseCaseStreamSpecOption).isFalse()
+ } else {
+ hasStreamUseCaseStreamSpecOption = false
+ }
+ }
+ }
+ suggestedStreamSpecsForOldSurfaces.entries.forEach {
+ if (it.value.implementationOptions?.containsOption(
+ StreamUseCaseUtil.STREAM_USE_CASE_STREAM_SPEC_OPTION
+ ) == true
+ ) {
+ if (hasStreamUseCaseStreamSpecOption != null) {
+ assertThat(hasStreamUseCaseStreamSpecOption).isTrue()
+ } else {
+ hasStreamUseCaseStreamSpecOption = true
+ }
+ } else {
+ if (hasStreamUseCaseStreamSpecOption != null) {
+ assertThat(hasStreamUseCaseStreamSpecOption).isFalse()
+ } else {
+ hasStreamUseCaseStreamSpecOption = false
+ }
+ }
+ }
+ return resultPair
}
private fun getUseCaseToConfigMap(useCases: List<UseCase>): Map<UseCase, UseCaseConfig<*>> {
@@ -2958,6 +3005,86 @@
assertThat(resultList).containsExactlyElementsIn(expectedResultList).inOrder()
}
+ @Config(minSdk = Build.VERSION_CODES.TIRAMISU)
+ @Test
+ fun canPopulateStreamUseCaseStreamSpecOption_jpeg() {
+ val jpegUseCase =
+ createUseCase(CaptureType.IMAGE_CAPTURE, streamUseCaseOverride = true) // JPEG
+ val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+ put(jpegUseCase, MAXIMUM_SIZE)
+ }
+ val resultPair = getSuggestedSpecsAndVerify(useCaseExpectedResultMap)
+ assertThat(resultPair.first.size).isEqualTo(1)
+ assertThat(
+ resultPair.first[jpegUseCase.currentConfig]!!.implementationOptions!!.retrieveOption(
+ StreamUseCaseUtil.STREAM_USE_CASE_STREAM_SPEC_OPTION
+ )
+ ).isEqualTo(STREAM_USE_CASE_OVERRIDE)
+ }
+
+ @Config(minSdk = Build.VERSION_CODES.TIRAMISU)
+ @Test
+ fun throwException_PopulateStreamUseCaseStreamSpecOption_notFullyOverride() {
+ val jpegUseCase =
+ createUseCase(CaptureType.IMAGE_CAPTURE, streamUseCaseOverride = true) // JPEG
+ val yuvUseCase =
+ createUseCase(CaptureType.PREVIEW, streamUseCaseOverride = false) // PREVIEW
+ val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+ put(jpegUseCase, MAXIMUM_SIZE)
+ put(yuvUseCase, PREVIEW_SIZE)
+ }
+ assertThrows(IllegalArgumentException::class.java) {
+ getSuggestedSpecsAndVerify(useCaseExpectedResultMap)
+ }
+ }
+
+ @Config(minSdk = Build.VERSION_CODES.TIRAMISU)
+ @Test
+ fun skipPopulateStreamUseCaseStreamSpecOption_unsupportedCombination() {
+ val useCase1 =
+ createUseCase(CaptureType.PREVIEW, streamUseCaseOverride = true) // PREVIEW
+ val useCase2 =
+ createUseCase(CaptureType.PREVIEW, streamUseCaseOverride = true) // PREVIEW
+ val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+ put(useCase1, PREVIEW_SIZE)
+ put(useCase2, PREVIEW_SIZE)
+ }
+ // PRIV + PRIV is supported by the Ultra-high table but not Stream use case
+ val resultPair = getSuggestedSpecsAndVerify(
+ useCaseExpectedResultMap, cameraMode = CameraMode.ULTRA_HIGH_RESOLUTION_CAMERA,
+ )
+ assertThat(resultPair.first.size).isEqualTo(2)
+ assertThat(
+ resultPair.first[useCase1.currentConfig]!!.implementationOptions!!.containsOption(
+ StreamUseCaseUtil.STREAM_USE_CASE_STREAM_SPEC_OPTION
+ )
+ ).isFalse()
+ assertThat(
+ resultPair.first[useCase2.currentConfig]!!.implementationOptions!!.containsOption(
+ StreamUseCaseUtil.STREAM_USE_CASE_STREAM_SPEC_OPTION
+ )
+ ).isFalse()
+ }
+
+ @Config(minSdk = 21, maxSdk = 32)
+ @Test
+ fun skipPopulateStreamUseCaseStreamSpecOption_unsupportedOs() {
+ val jpegUseCase =
+ createUseCase(CaptureType.IMAGE_CAPTURE, streamUseCaseOverride = true) // JPEG
+ val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+ put(jpegUseCase, MAXIMUM_SIZE)
+ }
+ val resultPair = getSuggestedSpecsAndVerify(
+ useCaseExpectedResultMap,
+ )
+ assertThat(resultPair.first.size).isEqualTo(1)
+ assertThat(
+ resultPair.first[jpegUseCase.currentConfig]!!.implementationOptions!!.containsOption(
+ StreamUseCaseUtil.STREAM_USE_CASE_STREAM_SPEC_OPTION
+ )
+ ).isFalse()
+ }
+
/**
* Sets up camera according to the specified settings and initialize [CameraX].
*
@@ -2989,7 +3116,7 @@
maximumResolutionHighResolutionSupportedSizes: Array<Size>? = null,
dynamicRangeProfiles: DynamicRangeProfiles? = null,
default10BitProfile: Long? = null,
- capabilities: IntArray? = null
+ capabilities: IntArray? = null,
) {
setupCamera(
cameraId,
@@ -3002,7 +3129,7 @@
maximumResolutionHighResolutionSupportedSizes,
dynamicRangeProfiles,
default10BitProfile,
- capabilities
+ capabilities,
)
@LensFacing val lensFacingEnum = CameraUtil.getLensFacingEnumFromInt(
@@ -3062,7 +3189,7 @@
maximumResolutionHighResolutionSupportedSizes: Array<Size>? = null,
dynamicRangeProfiles: DynamicRangeProfiles? = null,
default10BitProfile: Long? = null,
- capabilities: IntArray? = null
+ capabilities: IntArray? = null,
) {
val mockMap = Mockito.mock(StreamConfigurationMap::class.java).also { map ->
supportedSizes?.let {
@@ -3195,6 +3322,20 @@
}
}
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU
+ ) {
+ val uc = longArrayOf(
+ CameraCharacteristics.SCALER_AVAILABLE_STREAM_USE_CASES_DEFAULT.toLong(),
+ CameraCharacteristics.SCALER_AVAILABLE_STREAM_USE_CASES_PREVIEW.toLong(),
+ CameraCharacteristics.SCALER_AVAILABLE_STREAM_USE_CASES_PREVIEW_VIDEO_STILL
+ .toLong(),
+ CameraCharacteristics.SCALER_AVAILABLE_STREAM_USE_CASES_STILL_CAPTURE.toLong(),
+ CameraCharacteristics.SCALER_AVAILABLE_STREAM_USE_CASES_VIDEO_CALL.toLong(),
+ CameraCharacteristics.SCALER_AVAILABLE_STREAM_USE_CASES_VIDEO_RECORD.toLong()
+ )
+ set(CameraCharacteristics.SCALER_AVAILABLE_STREAM_USE_CASES, uc)
+ }
+
capabilities?.let {
set(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES, it)
}
@@ -3267,8 +3408,18 @@
targetFrameRate: Range<Int>? = null,
dynamicRange: DynamicRange = DynamicRange.UNSPECIFIED
): UseCase {
+ return createUseCase(captureType, targetFrameRate, dynamicRange, false)
+ }
+
+ private fun createUseCase(
+ captureType: CaptureType,
+ targetFrameRate: Range<Int>? = null,
+ dynamicRange: DynamicRange = DynamicRange.UNSPECIFIED,
+ streamUseCaseOverride: Boolean
+ ): UseCase {
val builder = FakeUseCaseConfig.Builder(
captureType, when (captureType) {
+ CaptureType.PREVIEW -> ImageFormat.PRIVATE
CaptureType.IMAGE_CAPTURE -> ImageFormat.JPEG
CaptureType.IMAGE_ANALYSIS -> ImageFormat.YUV_420_888
else -> INTERNAL_DEFINED_IMAGE_FORMAT_PRIVATE
@@ -3283,6 +3434,13 @@
dynamicRange
)
+ if (streamUseCaseOverride) {
+ builder.mutableConfig.insertOption(
+ Camera2ImplConfig.STREAM_USE_CASE_OPTION,
+ STREAM_USE_CASE_OVERRIDE
+ )
+ }
+
return builder.build()
}