[go: nahoru, domu]

blob: ffd508ba0279b8d7522c1789830e6f5ca4d5721c [file] [log] [blame]
/*
* Copyright 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package androidx.camera.camera2.internal;
import android.content.Context;
import android.graphics.ImageFormat;
import android.graphics.Point;
import android.graphics.SurfaceTexture;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraManager;
import android.hardware.camera2.params.StreamConfigurationMap;
import android.media.CamcorderProfile;
import android.os.Build;
import android.util.Pair;
import android.util.Rational;
import android.util.Size;
import android.view.Surface;
import android.view.WindowManager;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.camera.core.AspectRatio;
import androidx.camera.core.CameraX;
import androidx.camera.core.impl.ImageFormatConstants;
import androidx.camera.core.impl.ImageOutputConfig;
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.SurfaceSizeDefinition;
import androidx.camera.core.impl.UseCaseConfig;
import androidx.core.util.Preconditions;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Camera device supported surface configuration combinations
*
* <p>{@link android.hardware.camera2.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 a list of surface combinations that are guaranteed to
* support for this camera device.
*/
final class SupportedSurfaceCombination {
private static final Size MAX_PREVIEW_SIZE = new Size(1920, 1080);
private static final Size DEFAULT_SIZE = new Size(640, 480);
private static final Size ZERO_SIZE = new Size(0, 0);
private static final Size QUALITY_2160P_SIZE = new Size(3840, 2160);
private static final Size QUALITY_1080P_SIZE = new Size(1920, 1080);
private static final Size QUALITY_720P_SIZE = new Size(1280, 720);
private static final Size QUALITY_480P_SIZE = new Size(720, 480);
private static final int ALIGN16 = 16;
private static final Rational ASPECT_RATIO_4_3 = new Rational(4, 3);
private static final Rational ASPECT_RATIO_3_4 = new Rational(3, 4);
private static final Rational ASPECT_RATIO_16_9 = new Rational(16, 9);
private static final Rational ASPECT_RATIO_9_16 = new Rational(9, 16);
private final List<SurfaceCombination> mSurfaceCombinations = new ArrayList<>();
private final Map<Integer, Size> mMaxSizeCache = new HashMap<>();
private String mCameraId;
private CameraCharacteristics mCharacteristics;
private int mHardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY;
private boolean mIsRawSupported = false;
private boolean mIsBurstCaptureSupported = false;
private SurfaceSizeDefinition mSurfaceSizeDefinition;
private CamcorderProfileHelper mCamcorderProfileHelper;
SupportedSurfaceCombination(
Context context, String cameraId, CamcorderProfileHelper camcorderProfileHelper) {
mCameraId = cameraId;
mCamcorderProfileHelper = camcorderProfileHelper;
init(context);
}
private SupportedSurfaceCombination() {
}
String getCameraId() {
return mCameraId;
}
boolean isRawSupported() {
return mIsRawSupported;
}
boolean isBurstCaptureSupported() {
return mIsBurstCaptureSupported;
}
/**
* Check whether the input surface configuration list is under the capability of any combination
* of this object.
*
* @param surfaceConfigList the surface configuration list to be compared
* @return the check result that whether it could be supported
*/
boolean checkSupported(List<SurfaceConfig> surfaceConfigList) {
boolean isSupported = false;
for (SurfaceCombination surfaceCombination : mSurfaceCombinations) {
isSupported = surfaceCombination.isSupported(surfaceConfigList);
if (isSupported) {
break;
}
}
return isSupported;
}
/**
* Transform to a SurfaceConfig object with image format and size info
*
* @param imageFormat the image format info for the surface configuration object
* @param size the size info for the surface configuration object
* @return new {@link SurfaceConfig} object
*/
SurfaceConfig transformSurfaceConfig(int imageFormat, Size size) {
ConfigType configType;
ConfigSize configSize = ConfigSize.NOT_SUPPORT;
if (getAllOutputSizesByFormat(imageFormat) == null) {
throw new IllegalArgumentException(
"Can not get supported output size for the format: " + imageFormat);
}
/*
* PRIV refers to any target whose available sizes are found using
* StreamConfigurationMap.getOutputSizes(Class) with no direct application-visible format,
* YUV refers to a target Surface using the ImageFormat.YUV_420_888 format, JPEG refers to
* the ImageFormat.JPEG format, and RAW refers to the ImageFormat.RAW_SENSOR format.
*/
if (imageFormat == ImageFormat.YUV_420_888) {
configType = ConfigType.YUV;
} else if (imageFormat == ImageFormat.JPEG) {
configType = ConfigType.JPEG;
} else if (imageFormat == ImageFormat.RAW_SENSOR) {
configType = ConfigType.RAW;
} else {
configType = ConfigType.PRIV;
}
Size maxSize = fetchMaxSize(imageFormat);
// Compare with surface size definition to determine the surface configuration size
if (size.getWidth() * size.getHeight()
<= mSurfaceSizeDefinition.getAnalysisSize().getWidth()
* mSurfaceSizeDefinition.getAnalysisSize().getHeight()) {
configSize = ConfigSize.ANALYSIS;
} else if (size.getWidth() * size.getHeight()
<= mSurfaceSizeDefinition.getPreviewSize().getWidth()
* mSurfaceSizeDefinition.getPreviewSize().getHeight()) {
configSize = ConfigSize.PREVIEW;
} else if (size.getWidth() * size.getHeight()
<= mSurfaceSizeDefinition.getRecordSize().getWidth()
* mSurfaceSizeDefinition.getRecordSize().getHeight()) {
configSize = ConfigSize.RECORD;
} else if (size.getWidth() * size.getHeight() <= maxSize.getWidth() * maxSize.getHeight()) {
configSize = ConfigSize.MAXIMUM;
}
return SurfaceConfig.create(configType, configSize);
}
Map<UseCaseConfig<?>, Size> getSuggestedResolutions(
List<SurfaceConfig> existingSurfaces, List<UseCaseConfig<?>> newUseCaseConfigs) {
Map<UseCaseConfig<?>, Size> suggestedResolutionsMap = new HashMap<>();
// Get the index order list by the use case priority for finding stream configuration
List<Integer> useCasesPriorityOrder = getUseCasesPriorityOrder(newUseCaseConfigs);
List<List<Size>> supportedOutputSizesList = new ArrayList<>();
// Collect supported output sizes for all use cases
for (Integer index : useCasesPriorityOrder) {
List<Size> supportedOutputSizes =
getSupportedOutputSizes(newUseCaseConfigs.get(index));
supportedOutputSizesList.add(supportedOutputSizes);
}
// Get all possible size arrangements
List<List<Size>> allPossibleSizeArrangements =
getAllPossibleSizeArrangements(supportedOutputSizesList);
// 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<>(existingSurfaces);
// 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));
surfaceConfigList.add(transformSurfaceConfig(newUseCase.getInputFormat(), size));
}
// Check whether the SurfaceConfig combination can be supported
if (checkSupported(surfaceConfigList)) {
for (UseCaseConfig<?> useCaseConfig : newUseCaseConfigs) {
suggestedResolutionsMap.put(
useCaseConfig,
possibleSizeList.get(
useCasesPriorityOrder.indexOf(
newUseCaseConfigs.indexOf(useCaseConfig))));
}
break;
}
}
return suggestedResolutionsMap;
}
// If the device is LEGACY + Android 5.0, the aspect ratio need to be corrected, because
// there is a bug which was fixed in L MR1.
boolean requiresCorrectedAspectRatio() {
return mHardwareLevel == CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY
&& Build.VERSION.SDK_INT == 21;
}
// Gets the corrected aspect ratio due to device constraints or null if no correction is needed.
Rational getCorrectedAspectRatio(@ImageOutputConfig.RotationValue int targetRotation) {
Rational outputRatio = null;
/*
* If the device is LEGACY + Android 5.0, then return the same aspect ratio as maximum JPEG
* resolution. The Camera2 LEGACY mode API always sends the HAL a configure call with the
* same aspect ratio as the maximum JPEG resolution, and do the cropping/scaling before
* returning the output. There is a bug because of a flipped scaling factor in the
* intermediate texture transform matrix, and it was fixed in L MR1.
*/
if (mHardwareLevel == CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY
&& Build.VERSION.SDK_INT == 21) {
Size maxJpegSize = fetchMaxSize(ImageFormat.JPEG);
outputRatio = new Rational(maxJpegSize.getWidth(), maxJpegSize.getHeight());
outputRatio = rotateAspectRatioByRotation(outputRatio, targetRotation);
}
return outputRatio;
}
SurfaceSizeDefinition getSurfaceSizeDefinition() {
return mSurfaceSizeDefinition;
}
private Size fetchMaxSize(int imageFormat) {
Size size = mMaxSizeCache.get(imageFormat);
if (size != null) {
return size;
}
Size maxSize = getMaxOutputSizeByFormat(imageFormat);
mMaxSizeCache.put(imageFormat, maxSize);
return maxSize;
}
private List<Integer> getUseCasesPriorityOrder(List<UseCaseConfig<?>> newUseCaseConfigs) {
List<Integer> priorityOrder = new ArrayList<>();
/*
* Once the stream resource is occupied by one use case, it will impact the other use cases.
* Therefore, we need to define the priority for stream resource usage. For the use cases
* with the higher priority, we will try to find the best one for them in priority as
* possible.
*/
List<Integer> priorityValueList = new ArrayList<>();
for (UseCaseConfig<?> config : newUseCaseConfigs) {
int priority = config.getSurfaceOccupancyPriority(0);
if (!priorityValueList.contains(priority)) {
priorityValueList.add(priority);
}
}
Collections.sort(priorityValueList);
// Reverse the priority value list in descending order since larger value means higher
// priority
Collections.reverse(priorityValueList);
for (int priorityValue : priorityValueList) {
for (UseCaseConfig<?> config : newUseCaseConfigs) {
if (priorityValue == config.getSurfaceOccupancyPriority(0)) {
priorityOrder.add(newUseCaseConfigs.indexOf(config));
}
}
}
return priorityOrder;
}
@VisibleForTesting
List<Size> getSupportedOutputSizes(UseCaseConfig<?> config) {
int imageFormat = config.getInputFormat();
ImageOutputConfig imageOutputConfig = (ImageOutputConfig) config;
Size[] outputSizes = getAllOutputSizesByFormat(imageFormat, imageOutputConfig);
List<Size> outputSizeCandidates = new ArrayList<>();
Size maxSize = imageOutputConfig.getMaxResolution(getMaxOutputSizeByFormat(imageFormat));
int targetRotation = imageOutputConfig.getTargetRotation(Surface.ROTATION_0);
// Sort the output sizes. The Comparator result must be reversed to have a descending order
// result.
Arrays.sort(outputSizes, new CompareSizesByArea(true));
// Calibrate targetSize by the target rotation value.
Size targetSize = imageOutputConfig.getTargetResolution(ZERO_SIZE);
if (isRotationNeeded(targetRotation)) {
targetSize = new Size(/* width= */targetSize.getHeight(), /* height=
*/targetSize.getWidth());
}
Size minSize = DEFAULT_SIZE;
int defaultSizeArea = getArea(DEFAULT_SIZE);
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 = new Size(0, 0);
} else if (!targetSize.equals(ZERO_SIZE) && getArea(targetSize) < defaultSizeArea) {
minSize = targetSize;
}
// Filter out the ones that exceed the maximum size and the minimum size.
for (Size outputSize : outputSizes) {
if (getArea(outputSize) <= getArea(maxSize)
&& getArea(outputSize) >= getArea(minSize)) {
outputSizeCandidates.add(outputSize);
}
}
if (outputSizeCandidates.isEmpty()) {
throw new IllegalArgumentException(
"Can not get supported output size under supported maximum for the format: "
+ imageFormat);
}
// 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.
List<Size> sizesMatchAspectRatio = new ArrayList<>();
List<Size> sizesMismatchAspectRatio = new ArrayList<>();
Rational aspectRatio = null;
if (imageOutputConfig.hasTargetAspectRatio()) {
// Checks the sensor orientation.
boolean isSensorLandscapeOrientation = isRotationNeeded(Surface.ROTATION_0);
@AspectRatio.Ratio int targetAspectRatio = imageOutputConfig.getTargetAspectRatio();
switch (targetAspectRatio) {
case AspectRatio.RATIO_4_3:
aspectRatio =
isSensorLandscapeOrientation ? ASPECT_RATIO_4_3 : ASPECT_RATIO_3_4;
break;
case AspectRatio.RATIO_16_9:
aspectRatio =
isSensorLandscapeOrientation ? ASPECT_RATIO_16_9 : ASPECT_RATIO_9_16;
break;
default:
// Unhandled event.
}
} else {
aspectRatio = imageOutputConfig.getTargetAspectRatioCustom(null);
aspectRatio = rotateAspectRatioByRotation(aspectRatio, targetRotation);
}
for (Size outputSize : outputSizeCandidates) {
// If no target aspect ratio is set, all sizes will be considered to match the target
// aspect ratio and then added to the matched list. Or, those sizes that are really
// matching the target aspect ratio will also be added to the matched list.
if (aspectRatio == null || hasMatchingAspectRatio(outputSize, aspectRatio)) {
if (!sizesMatchAspectRatio.contains(outputSize)) {
sizesMatchAspectRatio.add(outputSize);
}
} else {
if (!sizesMismatchAspectRatio.contains(outputSize)) {
sizesMismatchAspectRatio.add(outputSize);
}
}
}
// Sort mismatching results by how close they are to the target aspect ratio.
if (aspectRatio != null) {
Collections.sort(sizesMismatchAspectRatio,
new CompareSizesByDistanceToTargetRatio(aspectRatio.floatValue()));
}
// Check the default resolution if the target resolution is not set
targetSize = targetSize.equals(ZERO_SIZE) ? imageOutputConfig.getDefaultResolution(
ZERO_SIZE) : targetSize;
// If the target resolution is set, use it to find the minimum one from big enough items
if (!targetSize.equals(ZERO_SIZE)) {
removeSupportedSizesByTargetSize(sizesMatchAspectRatio, targetSize);
removeSupportedSizesByTargetSizeAndAspectRatio(sizesMismatchAspectRatio, targetSize);
}
List<Size> supportedResolutions = new ArrayList<>();
// No need to sort again since the source list has been sorted previously
supportedResolutions.addAll(sizesMatchAspectRatio);
supportedResolutions.addAll(sizesMismatchAspectRatio);
return supportedResolutions;
}
// Use target rotation to calibrate the aspect ratio.
private Rational rotateAspectRatioByRotation(Rational aspectRatio, int targetRotation) {
Rational outputRatio = aspectRatio;
// Calibrates the aspect ratio with the display and sensor rotation degrees values.
// Otherwise, retrieves default aspect ratio for the target use case.
if (aspectRatio != null && isRotationNeeded(targetRotation)) {
outputRatio = new Rational(aspectRatio.getDenominator(),
aspectRatio.getNumerator());
}
return outputRatio;
}
private boolean isRotationNeeded(int targetRotation) {
int sensorRotationDegrees = CameraX.getCameraInfo(mCameraId).getSensorRotationDegrees(
targetRotation);
return sensorRotationDegrees == 90 || sensorRotationDegrees == 270;
}
static boolean hasMatchingAspectRatio(Size resolution, Rational aspectRatio) {
boolean isMatch = false;
if (aspectRatio == null) {
isMatch = false;
} else if (aspectRatio.equals(
new Rational(resolution.getWidth(), resolution.getHeight()))) {
isMatch = true;
} else if (getArea(resolution) >= getArea(DEFAULT_SIZE)) {
// Only do mod 16 calculation if the size is equal to or larger than 640x480. It is
// because the aspect ratio will be affected critically by mod 16 calculation if the
// size is small. It may result in unexpected outcome such like 256x144 will be
// considered as 18.5:9.
isMatch = isPossibleMod16FromAspectRatio(resolution, aspectRatio);
}
return isMatch;
}
private static boolean isPossibleMod16FromAspectRatio(Size resolution, Rational aspectRatio) {
int width = resolution.getWidth();
int height = resolution.getHeight();
Rational invAspectRatio = new Rational(/* numerator= */aspectRatio.getDenominator(),
/* denominator= */aspectRatio.getNumerator());
if (width % 16 == 0 && height % 16 == 0) {
return ratioIntersectsMod16Segment(Math.max(0, height - ALIGN16), width, aspectRatio)
|| ratioIntersectsMod16Segment(Math.max(0, width - ALIGN16), height,
invAspectRatio);
} else if (width % 16 == 0) {
return ratioIntersectsMod16Segment(height, width, aspectRatio);
} else if (height % 16 == 0) {
return ratioIntersectsMod16Segment(width, height, invAspectRatio);
}
return false;
}
private static int getArea(Size size) {
return size.getWidth() * size.getHeight();
}
private static boolean ratioIntersectsMod16Segment(int height, int mod16Width,
Rational aspectRatio) {
Preconditions.checkArgument(mod16Width % 16 == 0);
double aspectRatioWidth =
height * aspectRatio.getNumerator() / (double) aspectRatio.getDenominator();
return aspectRatioWidth > Math.max(0, mod16Width - ALIGN16) && aspectRatioWidth < (
mod16Width + ALIGN16);
}
/**
* Removes unnecessary big enough sizes by target size and aspect ratio.
*
* <p>The input supportedSizesList is sorted by CompareSizesByDistanceToTargetRatio that items
* of same aspect ratio will be put together. The removing process will operate for each
* different aspect ratio size bucket to only keep one big-enough size if there is.
*/
private void removeSupportedSizesByTargetSizeAndAspectRatio(List<Size> supportedSizesList,
Size targetSize) {
if (supportedSizesList == null || supportedSizesList.isEmpty()) {
return;
}
Map<Rational, Size> bigEnoughSizeMap = new HashMap<>();
List<Size> removeSizes = new ArrayList<>();
// Removing sizes for mismatching aspect ratio sizes list should be handled according
// to different aspect ratio sizes set because the input sizes list may be sorted by
// CompareSizesByDistanceToTargetRatio. Put the big enough size into map. The size
// finally put into map should be the smallest but big enough one for the sizes of
// same aspect ratio.
Rational currentRationalKey = null;
for (int i = 0; i < supportedSizesList.size(); i++) {
Size outputSize = supportedSizesList.get(i);
if (outputSize.getWidth() >= targetSize.getWidth()
&& outputSize.getHeight() >= targetSize.getHeight()) {
Rational rational = new Rational(outputSize.getWidth(), outputSize.getHeight());
// Some sizes belong to the same aspect ratio in mod 16 consideration.
if (currentRationalKey == null || !hasMatchingAspectRatio(outputSize,
currentRationalKey)) {
currentRationalKey = rational;
}
Size originalBigEnoughSize = bigEnoughSizeMap.get(currentRationalKey);
// There is smaller big-enough size candidate. Therefore, previous one can be
// removed from the list.
if (originalBigEnoughSize != null) {
removeSizes.add(originalBigEnoughSize);
}
bigEnoughSizeMap.put(currentRationalKey, outputSize);
}
}
// Remove the additional items that is larger than the big enough items
supportedSizesList.removeAll(removeSizes);
}
/**
* Removes unnecessary big enough sizes by target size.
*/
private void removeSupportedSizesByTargetSize(List<Size> supportedSizesList,
Size targetSize) {
if (supportedSizesList == null || supportedSizesList.isEmpty()) {
return;
}
int indexBigEnough = -1;
List<Size> removeSizes = new ArrayList<>();
// Get the index of the item that is big enough for the view size in matched list
for (int i = 0; i < supportedSizesList.size(); i++) {
Size outputSize = supportedSizesList.get(i);
if (outputSize.getWidth() >= targetSize.getWidth()
&& outputSize.getHeight() >= targetSize.getHeight()) {
// New big enough size is found. Adding previous one to remove sizes list.
if (indexBigEnough >= 0) {
removeSizes.add(supportedSizesList.get(indexBigEnough));
}
indexBigEnough = i;
} else {
break;
}
}
// Remove the additional items that is larger than the big enough item
supportedSizesList.removeAll(removeSizes);
}
private List<List<Size>> getAllPossibleSizeArrangements(
List<List<Size>> supportedOutputSizesList) {
int totalArrangementsCount = 1;
for (List<Size> supportedOutputSizes : supportedOutputSizesList) {
totalArrangementsCount *= supportedOutputSizes.size();
}
// If totalArrangementsCount is 0 means that there may some problem to get
// supportedOutputSizes
// for some use case
if (totalArrangementsCount == 0) {
throw new IllegalArgumentException("Failed to find supported resolutions.");
}
List<List<Size>> allPossibleSizeArrangements = new ArrayList<>();
// Initialize allPossibleSizeArrangements for the following operations
for (int i = 0; i < totalArrangementsCount; i++) {
List<Size> sizeList = new ArrayList<>();
allPossibleSizeArrangements.add(sizeList);
}
/*
* Try to list out all possible arrangements by attaching all possible size of each column
* in sequence. We have generated supportedOutputSizesList by the priority order for
* different use cases. And the supported outputs sizes for each use case are also arranged
* from large to small. Therefore, the earlier size arrangement in the result list will be
* the better one to choose if finally it won't exceed the camera device's stream
* combination capability.
*/
int currentRunCount = totalArrangementsCount;
int nextRunCount = currentRunCount / supportedOutputSizesList.get(0).size();
for (int currentIndex = 0; currentIndex < supportedOutputSizesList.size(); currentIndex++) {
List<Size> supportedOutputSizes = supportedOutputSizesList.get(currentIndex);
for (int i = 0; i < totalArrangementsCount; i++) {
List<Size> surfaceConfigList = allPossibleSizeArrangements.get(i);
surfaceConfigList.add(
supportedOutputSizes.get((i % currentRunCount) / nextRunCount));
}
if (currentIndex < supportedOutputSizesList.size() - 1) {
currentRunCount = nextRunCount;
nextRunCount =
currentRunCount / supportedOutputSizesList.get(currentIndex + 1).size();
}
}
return allPossibleSizeArrangements;
}
@Nullable
private Size[] getAllOutputSizesByFormat(int imageFormat) {
return getAllOutputSizesByFormat(imageFormat, null);
}
@Nullable
private Size[] getAllOutputSizesByFormat(int imageFormat, @Nullable ImageOutputConfig config) {
Size[] outputSizes = null;
// Try to retrieve customized supported resolutions from config.
List<Pair<Integer, Size[]>> formatResolutionsPairList = null;
if (config != null) {
formatResolutionsPairList = config.getSupportedResolutions(null);
}
if (formatResolutionsPairList != null) {
for (Pair<Integer, Size[]> formatResolutionPair : formatResolutionsPairList) {
if (formatResolutionPair.first == imageFormat) {
outputSizes = formatResolutionPair.second;
break;
}
}
}
// Try to retrieve supported resolutions if there is no customization.
if (outputSizes == null) {
if (mCharacteristics == null) {
throw new IllegalStateException("CameraCharacteristics is null.");
}
StreamConfigurationMap map =
mCharacteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
if (map == null) {
throw new IllegalArgumentException(
"Can not get supported output size for the format: " + imageFormat);
}
if (Build.VERSION.SDK_INT < 23
&& imageFormat == ImageFormatConstants.INTERNAL_DEFINED_IMAGE_FORMAT_PRIVATE) {
// This is a little tricky that 0x22 that is internal defined in
// StreamConfigurationMap.java to be equal to ImageFormat.PRIVATE that is public
// after Android level 23 but not public in Android L. Use {@link SurfaceTexture}
// or {@link MediaCodec} will finally mapped to 0x22 in StreamConfigurationMap to
// retrieve the output sizes information.
outputSizes = map.getOutputSizes(SurfaceTexture.class);
} else {
outputSizes = map.getOutputSizes(imageFormat);
}
}
if (outputSizes == null) {
throw new IllegalArgumentException(
"Can not get supported output size for the format: " + imageFormat);
}
// Sort the output sizes. The Comparator result must be reversed to have a descending order
// result.
Arrays.sort(outputSizes, new CompareSizesByArea(true));
return outputSizes;
}
/**
* Get max supported output size for specific image format
*
* @param imageFormat the image format info
* @return the max supported output size for the image format
*/
Size getMaxOutputSizeByFormat(int imageFormat) {
Size[] outputSizes = getAllOutputSizesByFormat(imageFormat);
return Collections.max(Arrays.asList(outputSizes), new CompareSizesByArea());
}
private void init(Context context) {
CameraManager cameraManager =
(CameraManager) context.getSystemService(Context.CAMERA_SERVICE);
WindowManager windowManager =
(WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
try {
generateSupportedCombinationList(cameraManager);
generateSurfaceSizeDefinition(windowManager);
} catch (CameraAccessException e) {
throw new IllegalArgumentException(
"Generate supported combination list and size definition fail - CameraId:"
+ mCameraId,
e);
}
checkCustomization();
}
List<SurfaceCombination> getLegacySupportedCombinationList() {
List<SurfaceCombination> combinationList = new ArrayList<>();
// (PRIV, MAXIMUM)
SurfaceCombination surfaceCombination1 = new SurfaceCombination();
surfaceCombination1.addSurfaceConfig(
SurfaceConfig.create(ConfigType.PRIV, ConfigSize.MAXIMUM));
combinationList.add(surfaceCombination1);
// (JPEG, MAXIMUM)
SurfaceCombination surfaceCombination2 = new SurfaceCombination();
surfaceCombination2.addSurfaceConfig(
SurfaceConfig.create(ConfigType.JPEG, ConfigSize.MAXIMUM));
combinationList.add(surfaceCombination2);
// (YUV, MAXIMUM)
SurfaceCombination surfaceCombination3 = new SurfaceCombination();
surfaceCombination3.addSurfaceConfig(
SurfaceConfig.create(ConfigType.YUV, ConfigSize.MAXIMUM));
combinationList.add(surfaceCombination3);
// Below two combinations are all supported in the combination
// (PRIV, PREVIEW) + (JPEG, MAXIMUM)
SurfaceCombination surfaceCombination4 = new SurfaceCombination();
surfaceCombination4.addSurfaceConfig(
SurfaceConfig.create(ConfigType.PRIV, ConfigSize.PREVIEW));
surfaceCombination4.addSurfaceConfig(
SurfaceConfig.create(ConfigType.JPEG, ConfigSize.MAXIMUM));
combinationList.add(surfaceCombination4);
// (YUV, PREVIEW) + (JPEG, MAXIMUM)
SurfaceCombination surfaceCombination5 = new SurfaceCombination();
surfaceCombination5.addSurfaceConfig(
SurfaceConfig.create(ConfigType.YUV, ConfigSize.PREVIEW));
surfaceCombination5.addSurfaceConfig(
SurfaceConfig.create(ConfigType.JPEG, ConfigSize.MAXIMUM));
combinationList.add(surfaceCombination5);
// (PRIV, PREVIEW) + (PRIV, PREVIEW)
SurfaceCombination surfaceCombination6 = new SurfaceCombination();
surfaceCombination6.addSurfaceConfig(
SurfaceConfig.create(ConfigType.PRIV, ConfigSize.PREVIEW));
surfaceCombination6.addSurfaceConfig(
SurfaceConfig.create(ConfigType.PRIV, ConfigSize.PREVIEW));
combinationList.add(surfaceCombination6);
// (PRIV, PREVIEW) + (YUV, PREVIEW)
SurfaceCombination surfaceCombination7 = new SurfaceCombination();
surfaceCombination7.addSurfaceConfig(
SurfaceConfig.create(ConfigType.PRIV, ConfigSize.PREVIEW));
surfaceCombination7.addSurfaceConfig(
SurfaceConfig.create(ConfigType.YUV, ConfigSize.PREVIEW));
combinationList.add(surfaceCombination7);
// (PRIV, PREVIEW) + (PRIV, PREVIEW) + (JPEG, MAXIMUM)
SurfaceCombination surfaceCombination8 = new SurfaceCombination();
surfaceCombination8.addSurfaceConfig(
SurfaceConfig.create(ConfigType.PRIV, ConfigSize.PREVIEW));
surfaceCombination8.addSurfaceConfig(
SurfaceConfig.create(ConfigType.YUV, ConfigSize.PREVIEW));
surfaceCombination8.addSurfaceConfig(
SurfaceConfig.create(ConfigType.JPEG, ConfigSize.MAXIMUM));
combinationList.add(surfaceCombination8);
return combinationList;
}
List<SurfaceCombination> getLimitedSupportedCombinationList() {
List<SurfaceCombination> combinationList = new ArrayList<>();
// (PRIV, PREVIEW) + (PRIV, RECORD)
SurfaceCombination surfaceCombination1 = new SurfaceCombination();
surfaceCombination1.addSurfaceConfig(
SurfaceConfig.create(ConfigType.PRIV, ConfigSize.PREVIEW));
surfaceCombination1.addSurfaceConfig(
SurfaceConfig.create(ConfigType.PRIV, ConfigSize.RECORD));
combinationList.add(surfaceCombination1);
// (PRIV, PREVIEW) + (YUV, RECORD)
SurfaceCombination surfaceCombination2 = new SurfaceCombination();
surfaceCombination2.addSurfaceConfig(
SurfaceConfig.create(ConfigType.PRIV, ConfigSize.PREVIEW));
surfaceCombination2.addSurfaceConfig(
SurfaceConfig.create(ConfigType.YUV, ConfigSize.RECORD));
combinationList.add(surfaceCombination2);
// (YUV, PREVIEW) + (YUV, RECORD)
SurfaceCombination surfaceCombination3 = new SurfaceCombination();
surfaceCombination3.addSurfaceConfig(
SurfaceConfig.create(ConfigType.YUV, ConfigSize.PREVIEW));
surfaceCombination3.addSurfaceConfig(
SurfaceConfig.create(ConfigType.YUV, ConfigSize.RECORD));
combinationList.add(surfaceCombination3);
// (PRIV, PREVIEW) + (PRIV, RECORD) + (JPEG, RECORD)
SurfaceCombination surfaceCombination4 = new SurfaceCombination();
surfaceCombination4.addSurfaceConfig(
SurfaceConfig.create(ConfigType.PRIV, ConfigSize.PREVIEW));
surfaceCombination4.addSurfaceConfig(
SurfaceConfig.create(ConfigType.PRIV, ConfigSize.RECORD));
surfaceCombination4.addSurfaceConfig(
SurfaceConfig.create(ConfigType.JPEG, ConfigSize.RECORD));
combinationList.add(surfaceCombination4);
// (PRIV, PREVIEW) + (YUV, RECORD) + (JPEG, RECORD)
SurfaceCombination surfaceCombination5 = new SurfaceCombination();
surfaceCombination5.addSurfaceConfig(
SurfaceConfig.create(ConfigType.PRIV, ConfigSize.PREVIEW));
surfaceCombination5.addSurfaceConfig(
SurfaceConfig.create(ConfigType.YUV, ConfigSize.RECORD));
surfaceCombination5.addSurfaceConfig(
SurfaceConfig.create(ConfigType.JPEG, ConfigSize.RECORD));
combinationList.add(surfaceCombination5);
// (YUV, PREVIEW) + (YUV, PREVIEW) + (JPEG, MAXIMUM)
SurfaceCombination surfaceCombination6 = new SurfaceCombination();
surfaceCombination6.addSurfaceConfig(
SurfaceConfig.create(ConfigType.YUV, ConfigSize.PREVIEW));
surfaceCombination6.addSurfaceConfig(
SurfaceConfig.create(ConfigType.YUV, ConfigSize.PREVIEW));
surfaceCombination6.addSurfaceConfig(
SurfaceConfig.create(ConfigType.JPEG, ConfigSize.MAXIMUM));
combinationList.add(surfaceCombination6);
return combinationList;
}
List<SurfaceCombination> getFullSupportedCombinationList() {
List<SurfaceCombination> combinationList = new ArrayList<>();
// (PRIV, PREVIEW) + (PRIV, MAXIMUM)
SurfaceCombination surfaceCombination1 = new SurfaceCombination();
surfaceCombination1.addSurfaceConfig(
SurfaceConfig.create(ConfigType.PRIV, ConfigSize.PREVIEW));
surfaceCombination1.addSurfaceConfig(
SurfaceConfig.create(ConfigType.PRIV, ConfigSize.MAXIMUM));
combinationList.add(surfaceCombination1);
// (PRIV, PREVIEW) + (YUV, MAXIMUM)
SurfaceCombination surfaceCombination2 = new SurfaceCombination();
surfaceCombination2.addSurfaceConfig(
SurfaceConfig.create(ConfigType.PRIV, ConfigSize.PREVIEW));
surfaceCombination2.addSurfaceConfig(
SurfaceConfig.create(ConfigType.YUV, ConfigSize.MAXIMUM));
combinationList.add(surfaceCombination2);
// (YUV, PREVIEW) + (YUV, MAXIMUM)
SurfaceCombination surfaceCombination3 = new SurfaceCombination();
surfaceCombination3.addSurfaceConfig(
SurfaceConfig.create(ConfigType.YUV, ConfigSize.PREVIEW));
surfaceCombination3.addSurfaceConfig(
SurfaceConfig.create(ConfigType.YUV, ConfigSize.MAXIMUM));
combinationList.add(surfaceCombination3);
// (PRIV, PREVIEW) + (PRIV, PREVIEW) + (JPEG, MAXIMUM)
SurfaceCombination surfaceCombination4 = new SurfaceCombination();
surfaceCombination4.addSurfaceConfig(
SurfaceConfig.create(ConfigType.PRIV, ConfigSize.PREVIEW));
surfaceCombination4.addSurfaceConfig(
SurfaceConfig.create(ConfigType.PRIV, ConfigSize.PREVIEW));
surfaceCombination4.addSurfaceConfig(
SurfaceConfig.create(ConfigType.JPEG, ConfigSize.MAXIMUM));
combinationList.add(surfaceCombination4);
// (YUV, ANALYSIS) + (PRIV, PREVIEW) + (YUV, MAXIMUM)
SurfaceCombination surfaceCombination5 = new SurfaceCombination();
surfaceCombination5.addSurfaceConfig(
SurfaceConfig.create(ConfigType.YUV, ConfigSize.ANALYSIS));
surfaceCombination5.addSurfaceConfig(
SurfaceConfig.create(ConfigType.PRIV, ConfigSize.PREVIEW));
surfaceCombination5.addSurfaceConfig(
SurfaceConfig.create(ConfigType.YUV, ConfigSize.MAXIMUM));
combinationList.add(surfaceCombination5);
// (YUV, ANALYSIS) + (YUV, PREVIEW) + (YUV, MAXIMUM)
SurfaceCombination surfaceCombination6 = new SurfaceCombination();
surfaceCombination6.addSurfaceConfig(
SurfaceConfig.create(ConfigType.YUV, ConfigSize.ANALYSIS));
surfaceCombination6.addSurfaceConfig(
SurfaceConfig.create(ConfigType.YUV, ConfigSize.PREVIEW));
surfaceCombination6.addSurfaceConfig(
SurfaceConfig.create(ConfigType.YUV, ConfigSize.MAXIMUM));
combinationList.add(surfaceCombination6);
return combinationList;
}
List<SurfaceCombination> getRAWSupportedCombinationList() {
List<SurfaceCombination> combinationList = new ArrayList<>();
// (RAW, MAXIMUM)
SurfaceCombination surfaceCombination1 = new SurfaceCombination();
surfaceCombination1.addSurfaceConfig(
SurfaceConfig.create(ConfigType.RAW, ConfigSize.MAXIMUM));
combinationList.add(surfaceCombination1);
// (PRIV, PREVIEW) + (RAW, MAXIMUM)
SurfaceCombination surfaceCombination2 = new SurfaceCombination();
surfaceCombination2.addSurfaceConfig(
SurfaceConfig.create(ConfigType.PRIV, ConfigSize.PREVIEW));
surfaceCombination2.addSurfaceConfig(
SurfaceConfig.create(ConfigType.RAW, ConfigSize.MAXIMUM));
combinationList.add(surfaceCombination2);
// (YUV, PREVIEW) + (RAW, MAXIMUM)
SurfaceCombination surfaceCombination3 = new SurfaceCombination();
surfaceCombination3.addSurfaceConfig(
SurfaceConfig.create(ConfigType.YUV, ConfigSize.PREVIEW));
surfaceCombination3.addSurfaceConfig(
SurfaceConfig.create(ConfigType.RAW, ConfigSize.MAXIMUM));
combinationList.add(surfaceCombination3);
// (PRIV, PREVIEW) + (PRIV, PREVIEW) + (RAW, MAXIMUM)
SurfaceCombination surfaceCombination4 = new SurfaceCombination();
surfaceCombination4.addSurfaceConfig(
SurfaceConfig.create(ConfigType.PRIV, ConfigSize.PREVIEW));
surfaceCombination4.addSurfaceConfig(
SurfaceConfig.create(ConfigType.PRIV, ConfigSize.PREVIEW));
surfaceCombination4.addSurfaceConfig(
SurfaceConfig.create(ConfigType.RAW, ConfigSize.MAXIMUM));
combinationList.add(surfaceCombination4);
// (PRIV, PREVIEW) + (YUV, PREVIEW) + (RAW, MAXIMUM)
SurfaceCombination surfaceCombination5 = new SurfaceCombination();
surfaceCombination5.addSurfaceConfig(
SurfaceConfig.create(ConfigType.PRIV, ConfigSize.PREVIEW));
surfaceCombination5.addSurfaceConfig(
SurfaceConfig.create(ConfigType.YUV, ConfigSize.PREVIEW));
surfaceCombination5.addSurfaceConfig(
SurfaceConfig.create(ConfigType.RAW, ConfigSize.MAXIMUM));
combinationList.add(surfaceCombination5);
// (YUV, PREVIEW) + (YUV, PREVIEW) + (RAW, MAXIMUM)
SurfaceCombination surfaceCombination6 = new SurfaceCombination();
surfaceCombination6.addSurfaceConfig(
SurfaceConfig.create(ConfigType.YUV, ConfigSize.PREVIEW));
surfaceCombination6.addSurfaceConfig(
SurfaceConfig.create(ConfigType.YUV, ConfigSize.PREVIEW));
surfaceCombination6.addSurfaceConfig(
SurfaceConfig.create(ConfigType.RAW, ConfigSize.MAXIMUM));
combinationList.add(surfaceCombination6);
// (PRIV, PREVIEW) + (JPEG, MAXIMUM) + (RAW, MAXIMUM)
SurfaceCombination surfaceCombination7 = new SurfaceCombination();
surfaceCombination7.addSurfaceConfig(
SurfaceConfig.create(ConfigType.PRIV, ConfigSize.PREVIEW));
surfaceCombination7.addSurfaceConfig(
SurfaceConfig.create(ConfigType.JPEG, ConfigSize.MAXIMUM));
surfaceCombination7.addSurfaceConfig(
SurfaceConfig.create(ConfigType.RAW, ConfigSize.MAXIMUM));
combinationList.add(surfaceCombination7);
// (YUV, PREVIEW) + (JPEG, MAXIMUM) + (RAW, MAXIMUM)
SurfaceCombination surfaceCombination8 = new SurfaceCombination();
surfaceCombination8.addSurfaceConfig(
SurfaceConfig.create(ConfigType.YUV, ConfigSize.PREVIEW));
surfaceCombination8.addSurfaceConfig(
SurfaceConfig.create(ConfigType.JPEG, ConfigSize.MAXIMUM));
surfaceCombination8.addSurfaceConfig(
SurfaceConfig.create(ConfigType.RAW, ConfigSize.MAXIMUM));
combinationList.add(surfaceCombination8);
return combinationList;
}
List<SurfaceCombination> getBurstSupportedCombinationList() {
List<SurfaceCombination> combinationList = new ArrayList<>();
// (PRIV, PREVIEW) + (PRIV, MAXIMUM)
SurfaceCombination surfaceCombination1 = new SurfaceCombination();
surfaceCombination1.addSurfaceConfig(
SurfaceConfig.create(ConfigType.PRIV, ConfigSize.PREVIEW));
surfaceCombination1.addSurfaceConfig(
SurfaceConfig.create(ConfigType.PRIV, ConfigSize.MAXIMUM));
combinationList.add(surfaceCombination1);
// (PRIV, PREVIEW) + (YUV, MAXIMUM)
SurfaceCombination surfaceCombination2 = new SurfaceCombination();
surfaceCombination2.addSurfaceConfig(
SurfaceConfig.create(ConfigType.PRIV, ConfigSize.PREVIEW));
surfaceCombination2.addSurfaceConfig(
SurfaceConfig.create(ConfigType.YUV, ConfigSize.MAXIMUM));
combinationList.add(surfaceCombination2);
// (YUV, PREVIEW) + (YUV, MAXIMUM)
SurfaceCombination surfaceCombination3 = new SurfaceCombination();
surfaceCombination3.addSurfaceConfig(
SurfaceConfig.create(ConfigType.YUV, ConfigSize.PREVIEW));
surfaceCombination3.addSurfaceConfig(
SurfaceConfig.create(ConfigType.YUV, ConfigSize.MAXIMUM));
combinationList.add(surfaceCombination3);
return combinationList;
}
List<SurfaceCombination> getLevel3SupportedCombinationList() {
List<SurfaceCombination> combinationList = new ArrayList<>();
// (PRIV, PREVIEW) + (PRIV, ANALYSIS) + (YUV, MAXIMUM) + (RAW, MAXIMUM)
SurfaceCombination surfaceCombination1 = new SurfaceCombination();
surfaceCombination1.addSurfaceConfig(
SurfaceConfig.create(ConfigType.PRIV, ConfigSize.PREVIEW));
surfaceCombination1.addSurfaceConfig(
SurfaceConfig.create(ConfigType.PRIV, ConfigSize.ANALYSIS));
surfaceCombination1.addSurfaceConfig(
SurfaceConfig.create(ConfigType.YUV, ConfigSize.MAXIMUM));
surfaceCombination1.addSurfaceConfig(
SurfaceConfig.create(ConfigType.RAW, ConfigSize.MAXIMUM));
combinationList.add(surfaceCombination1);
// (PRIV, PREVIEW) + (PRIV, ANALYSIS) + (JPEG, MAXIMUM) + (RAW, MAXIMUM)
SurfaceCombination surfaceCombination2 = new SurfaceCombination();
surfaceCombination2.addSurfaceConfig(
SurfaceConfig.create(ConfigType.PRIV, ConfigSize.PREVIEW));
surfaceCombination2.addSurfaceConfig(
SurfaceConfig.create(ConfigType.PRIV, ConfigSize.ANALYSIS));
surfaceCombination2.addSurfaceConfig(
SurfaceConfig.create(ConfigType.JPEG, ConfigSize.MAXIMUM));
surfaceCombination2.addSurfaceConfig(
SurfaceConfig.create(ConfigType.RAW, ConfigSize.MAXIMUM));
combinationList.add(surfaceCombination2);
return combinationList;
}
private void generateSupportedCombinationList(CameraManager cameraManager)
throws CameraAccessException {
mCharacteristics = cameraManager.getCameraCharacteristics(mCameraId);
Integer keyValue = mCharacteristics.get(
CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
if (keyValue != null) {
mHardwareLevel = keyValue;
}
mSurfaceCombinations.addAll(getLegacySupportedCombinationList());
if (mHardwareLevel == CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
|| mHardwareLevel == CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL
|| mHardwareLevel == CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_3) {
mSurfaceCombinations.addAll(getLimitedSupportedCombinationList());
}
if (mHardwareLevel == CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL
|| mHardwareLevel == CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_3) {
mSurfaceCombinations.addAll(getFullSupportedCombinationList());
}
int[] availableCapabilities =
mCharacteristics.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
if (availableCapabilities != null) {
for (int capability : availableCapabilities) {
if (capability == CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW) {
mIsRawSupported = true;
} else if (capability
== CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE) {
mIsBurstCaptureSupported = true;
}
}
}
if (mIsRawSupported) {
mSurfaceCombinations.addAll(getRAWSupportedCombinationList());
}
if (mIsBurstCaptureSupported
&& mHardwareLevel == CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED) {
mSurfaceCombinations.addAll(getBurstSupportedCombinationList());
}
if (mHardwareLevel == CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_3) {
mSurfaceCombinations.addAll(getLevel3SupportedCombinationList());
}
}
private void checkCustomization() {
// TODO(b/119466260): Integrate found feasible stream combinations into supported list
}
// Utility classes and methods:
// *********************************************************************************************
private void generateSurfaceSizeDefinition(WindowManager windowManager) {
Size analysisSize = new Size(640, 480);
Size previewSize = getPreviewSize(windowManager);
Size recordSize = getRecordSize();
mSurfaceSizeDefinition =
SurfaceSizeDefinition.create(analysisSize, previewSize, recordSize);
}
/**
* PREVIEW refers to the best size match to the device's screen resolution, or to 1080p
* (1920x1080), whichever is smaller.
*/
private Size getPreviewSize(WindowManager windowManager) {
Point displaySize = new Point();
windowManager.getDefaultDisplay().getRealSize(displaySize);
Size displayViewSize;
if (displaySize.x > displaySize.y) {
displayViewSize = new Size(displaySize.x, displaySize.y);
} else {
displayViewSize = new Size(displaySize.y, displaySize.x);
}
// Limit the max preview size to under min(display size, 1080P) by comparing the area size
Size previewSize =
Collections.min(
Arrays.asList(
new Size(displayViewSize.getWidth(), displayViewSize.getHeight()),
MAX_PREVIEW_SIZE),
new CompareSizesByArea());
return previewSize;
}
/**
* RECORD refers to the camera device's maximum supported recording resolution, as determined by
* CamcorderProfile.
*/
private Size getRecordSize() {
Size recordSize = QUALITY_480P_SIZE;
// Check whether 2160P, 1080P, 720P, 480P are supported by CamcorderProfile
if (mCamcorderProfileHelper.hasProfile(
Integer.parseInt(mCameraId), CamcorderProfile.QUALITY_2160P)) {
recordSize = QUALITY_2160P_SIZE;
} else if (mCamcorderProfileHelper.hasProfile(
Integer.parseInt(mCameraId), CamcorderProfile.QUALITY_1080P)) {
recordSize = QUALITY_1080P_SIZE;
} else if (mCamcorderProfileHelper.hasProfile(
Integer.parseInt(mCameraId), CamcorderProfile.QUALITY_720P)) {
recordSize = QUALITY_720P_SIZE;
} else if (mCamcorderProfileHelper.hasProfile(
Integer.parseInt(mCameraId), CamcorderProfile.QUALITY_480P)) {
recordSize = QUALITY_480P_SIZE;
}
return recordSize;
}
/** Comparator based on area of the given {@link Size} objects. */
static final class CompareSizesByArea implements Comparator<Size> {
private boolean mReverse = false;
CompareSizesByArea() {
}
CompareSizesByArea(boolean reverse) {
mReverse = reverse;
}
@Override
public int compare(Size lhs, Size rhs) {
// We cast here to ensure the multiplications won't overflow
int result =
Long.signum(
(long) lhs.getWidth() * lhs.getHeight()
- (long) rhs.getWidth() * rhs.getHeight());
if (mReverse) {
result *= -1;
}
return result;
}
}
/** Comparator based on how close they are to the target aspect ratio. */
static final class CompareSizesByDistanceToTargetRatio implements Comparator<Size> {
private Float mTargetRatio;
CompareSizesByDistanceToTargetRatio(Float targetRatio) {
mTargetRatio = targetRatio;
}
@Override
public int compare(Size lhs, Size rhs) {
// Checks whether they are equal first since mod16 cases need to be considered.
if (hasMatchingAspectRatio(lhs, new Rational(rhs.getWidth(), rhs.getHeight()))) {
return 0;
}
final Float lhsRatio = lhs.getWidth() * 1.0f / lhs.getHeight();
final Float rhsRatio = rhs.getWidth() * 1.0f / rhs.getHeight();
final Float lhsRatioDelta = Math.abs(lhsRatio - mTargetRatio);
final Float rhsRatioDelta = Math.abs(rhsRatio - mTargetRatio);
int result = (int) Math.signum(lhsRatioDelta - rhsRatioDelta);
return result;
}
}
}