/*
* 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.core;
import android.util.Size;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.annotation.RestrictTo;
import androidx.camera.core.impl.SizeCoordinate;
/**
* A set of requirements and priorities used to select a resolution for the use case.
*
* <p>The resolution selection mechanism is determined by the following three steps:
* <ol>
* <li> Collect supported output sizes to the candidate resolution list
* <li> Determine the selecting priority of the candidate resolution list by the preference
* settings
* <li> Consider all the resolution selector settings of bound use cases to find the best
* resolution for each use case
* </ol>
*
* <p>For the first step, all supported resolution output sizes are put into the candidate
* resolution list as the base in the beginning.
*
* <p>ResolutionSelector provides the following two functions for applications to adjust the
* conditions of the candidate resolutions.
* <ul>
* <li> {@link Builder#setMaxResolution(Size)}
* <li> {@link Builder#setHighResolutionEnabled(boolean)}
* </ul>
*
* <p>For the second step, ResolutionSelector provides the following three functions for
* applications to determine which resolution has higher priority to be selected.
* <ul>
* <li> {@link Builder#setPreferredResolution(Size)}
* <li> {@link Builder#setPreferredResolutionByViewSize(Size)}
* <li> {@link Builder#setPreferredAspectRatio(int)}
* </ul>
*
* <p>The resolution that exactly matches the preferred resolution is selected in first priority.
* If the resolution can't be found, CameraX falls back to use the sizes of the preferred aspect
* ratio. In this case, the preferred resolution is treated as the minimal bounding size to find
* the best resolution.
*
* <p>Different types of use cases might have their own additional conditions. Please see the use
* case config builders’ {@code setResolutionSelector()} function to know the condition details
* for each type of use case.
*
* <p>For the third step, CameraX selects the final resolution for the use case based on the
* camera device's hardware level, capabilities and the bound use case combination. Applications
* can check which resolution is finally selected by using the use case's {@code
* getResolutionInfo()} function.
*
*/
@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public class ResolutionSelector {
@Nullable
private final Size mPreferredResolution;
private final SizeCoordinate mSizeCoordinate;
private final int mPreferredAspectRatio;
@Nullable
private final Size mMaxResolution;
private final boolean mIsHighResolutionEnabled;
ResolutionSelector(int preferredAspectRatio,
@Nullable Size preferredResolution,
@NonNull SizeCoordinate sizeCoordinate,
@Nullable Size maxResolution,
boolean isHighResolutionEnabled) {
mPreferredAspectRatio = preferredAspectRatio;
mPreferredResolution = preferredResolution;
mSizeCoordinate = sizeCoordinate;
mMaxResolution = maxResolution;
mIsHighResolutionEnabled = isHighResolutionEnabled;
}
/**
* Retrieves the preferred aspect ratio in the ResolutionSelector.
*
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
@AspectRatio.Ratio
public int getPreferredAspectRatio() {
return mPreferredAspectRatio;
}
/**
* Retrieves the preferred resolution in the ResolutionSelector.
*
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
@Nullable
public Size getPreferredResolution() {
return mPreferredResolution;
}
/**
* Retrieves the size coordinate of the preferred resolution in the ResolutionSelector.
*
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
@NonNull
public SizeCoordinate getSizeCoordinate() {
return mSizeCoordinate;
}
/**
* Returns the max resolution in the ResolutionSelector.
*
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
@Nullable
public Size getMaxResolution() {
return mMaxResolution;
}
/**
* Returns {@code true} if high resolutions are allowed to be selected, otherwise {@code false}.
*
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public boolean isHighResolutionEnabled() {
return mIsHighResolutionEnabled;
}
/**
* Builder for a {@link ResolutionSelector}.
*/
public static final class Builder {
@AspectRatio.Ratio
private int mPreferredAspectRatio = AspectRatio.RATIO_4_3;
@Nullable
private Size mPreferredResolution = null;
@NonNull
private SizeCoordinate mSizeCoordinate = SizeCoordinate.CAMERA_SENSOR;
@Nullable
private Size mMaxResolution = null;
private boolean mIsHighResolutionEnabled = false;
/**
* Creates a new Builder object.
*/
public Builder() {
}
private Builder(@NonNull ResolutionSelector selector) {
mPreferredAspectRatio = selector.getPreferredAspectRatio();
mPreferredResolution = selector.getPreferredResolution();
mSizeCoordinate = selector.getSizeCoordinate();
mMaxResolution = selector.getMaxResolution();
mIsHighResolutionEnabled = selector.isHighResolutionEnabled();
}
/**
* Generates a Builder from another {@link ResolutionSelector} object.
*
* @param selector an existing {@link ResolutionSelector}.
* @return the new Builder.
*/
@NonNull
public static Builder fromSelector(@NonNull ResolutionSelector selector) {
return new Builder(selector);
}
/**
* Sets the preferred aspect ratio that the output images are expected to have.
*
* <p>The aspect ratio is the ratio of width to height in the camera sensor's natural
* orientation. If set, CameraX finds the sizes that match the aspect ratio with priority
* . Among the sizes that match the aspect ratio, the larger the size, the higher the
* priority.
*
* <p>If CameraX can't find any available sizes that match the preferred aspect ratio,
* CameraX falls back to select the sizes with the nearest aspect ratio that can contain
* the full field of view of the sizes with preferred aspect ratio.
*
* <p>If preferred aspect ratio is not set, the default aspect ratio is
* {@link AspectRatio#RATIO_4_3}, which usually has largest field of view because most
* camera sensor are {@code 4:3}.
*
* <p>This API is useful for apps that want to capture images matching the {@code 16:9}
* display aspect ratio. Apps can set preferred aspect ratio as
* {@link AspectRatio#RATIO_16_9} to achieve this.
*
* <p>The actual aspect ratio of the output may differ from the specified preferred
* aspect ratio value. Application code should check the resulting output's resolution.
*
* @param preferredAspectRatio the aspect ratio you prefer to use.
* @return the current Builder.
*/
@NonNull
public Builder setPreferredAspectRatio(@AspectRatio.Ratio int preferredAspectRatio) {
mPreferredAspectRatio = preferredAspectRatio;
return this;
}
/**
* Sets the preferred resolution you expect to select. The resolution is expressed in the
* camera sensor's natural orientation (landscape), which means you can set the size
* retrieved from
* {@link android.hardware.camera2.params.StreamConfigurationMap#getOutputSizes} directly.
*
* <p>Once the preferred resolution is set, CameraX finds exactly matched size first
* regardless of the preferred aspect ratio. This API is useful for apps that want to
* select an exact size retrieved from
* {@link android.hardware.camera2.params.StreamConfigurationMap#getOutputSizes}.
*
* <p>If CameraX can't find the size that matches the preferred resolution, it attempts
* to establish a minimal bound for the given resolution. The actual resolution is the
* closest available resolution that is not smaller than the preferred resolution.
* However, if no resolution exists that is equal to or larger than the preferred
* resolution, the nearest available resolution smaller than the preferred resolution is
* chosen.
*
* <p>When the preferred resolution is used as a minimal bound, CameraX also considers
* the preferred aspect ratio to find the sizes that either match it or are close to it.
* Using preferred resolution as the minimal bound is useful for apps that want to shrink
* the size for the surface. For example, for apps that just show the camera preview in a
* small view, apps can specify a size smaller than display size. CameraX can effectively
* select a smaller size for better efficiency.
*
* <p>If both {@link Builder#setPreferredResolution(Size)} and
* {@link Builder#setPreferredResolutionByViewSize(Size)} are invoked, which one set
* later overrides the one set before.
*
* @param preferredResolution the preferred resolution expressed in the orientation of
* the device camera sensor coordinate to choose the preferred
* resolution from supported output sizes list.
* @return the current Builder.
*/
@NonNull
public Builder setPreferredResolution(@NonNull Size preferredResolution) {
mPreferredResolution = preferredResolution;
mSizeCoordinate = SizeCoordinate.CAMERA_SENSOR;
return this;
}
/**
* Sets the preferred resolution you expect to select. The resolution is expressed in the
* Android {@link View} coordinate system.
*
* <p>For phone devices, the sensor coordinate orientation usually has 90 degrees
* difference from the phone device display’s natural orientation. Depending on the
* display rotation value when the use case is bound, CameraX transforms the input
* resolution into the camera sensor's natural orientation to find the best suitable
* resolution.
*
* <p>Once the preferred resolution is set, CameraX finds the size that exactly matches
* the preferred resolution first regardless of the preferred aspect ratio.
*
* <p>If CameraX can't find the size that matches the preferred resolution, it attempts
* to establish a minimal bound for the given resolution. The actual resolution is the
* closest available resolution that is not smaller than the preferred resolution.
* However, if no resolution exists that is equal to or larger than the preferred
* resolution, the nearest available resolution smaller than the preferred resolution is
* chosen.
*
* <p>When the preferred resolution is used as a minimal bound, CameraX also considers
* the preferred aspect ratio to find the sizes that either match it or are close to it.
* Using Android {@link View} size as preferred resolution is useful for apps that want
* to shrink the size for the surface. For example, for apps that just show the camera
* preview in a small view, apps can specify the small size of Android {@link View}.
* CameraX can effectively select a smaller size for better efficiency.
*
* <p>If both {@link Builder#setPreferredResolution(Size)} and
* {@link Builder#setPreferredResolutionByViewSize(Size)} are invoked, the later setting
* overrides the former one.
*
* @param preferredResolutionByViewSize the preferred resolution expressed in the
* orientation of the app layout's Android
* {@link View} to choose the preferred resolution
* from supported output sizes list.
* @return the current Builder.
*/
@NonNull
public Builder setPreferredResolutionByViewSize(
@NonNull Size preferredResolutionByViewSize) {
mPreferredResolution = preferredResolutionByViewSize;
mSizeCoordinate = SizeCoordinate.ANDROID_VIEW;
return this;
}
/**
* Sets the max resolution condition for the use case.
*
* <p>The max resolution prevents the use case to select the sizes which either width or
* height exceeds the specified resolution.
*
* <p>The resolution should be expressed in the camera sensor's natural orientation
* (landscape).
*
* <p>For example, if applications want to select a resolution smaller than a specific
* resolution to have better performance, a {@link ResolutionSelector} which sets this
* specific resolution as the max resolution can be used. Or, if applications want to
* select a larger resolution for a {@link Preview} which has the default max resolution
* of the small one of device's screen size and 1080p (1920x1080), use a
* {@link ResolutionSelector} with max resolution.
*
* @param resolution the max resolution limitation to choose from supported output sizes
* list.
* @return the current Builder.
*/
@NonNull
public Builder setMaxResolution(@NonNull Size resolution) {
mMaxResolution = resolution;
return this;
}
/**
* Sets whether high resolutions are allowed to be selected for the use cases.
*
* <p>Calling this function allows the use case to select the high resolution output
* sizes if it is supported for the camera device.
*
* <p>When high resolution is enabled, if an {@link ImageCapture} with
* {@link ImageCapture#CAPTURE_MODE_ZERO_SHUTTER_LAG} mode is bound, the
* {@link ImageCapture#CAPTURE_MODE_ZERO_SHUTTER_LAG} mode is forced disabled.
*
* <p>When using the {@code camera-extensions} to enable an extension mode, even if high
* resolution is enabled, the supported high resolution output sizes are still excluded
* from the candidate resolution list.
*
* <p>When using the {@code camera-camera2} CameraX implementation, the supported
* high resolutions are retrieved from
* {@link android.hardware.camera2.params.StreamConfigurationMap#getHighResolutionOutputSizes(int)}.
* Be noticed that the high resolution sizes might cause the entire capture session to
* not meet the 20 fps frame rate. Even if only an ImageCapture use case selects a high
* resolution, it might still impact the FPS of the Preview, ImageAnalysis or
* VideoCapture use cases which are bound together. This function only takes effect on
* devices with
* {@link android.hardware.camera2.CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE}
* capability. For devices without
* {@link android.hardware.camera2.CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE}
* capability, all resolutions can be retrieved from
* {@link android.hardware.camera2.params.StreamConfigurationMap#getOutputSizes(int)},
* but it is not guaranteed to meet >= 20 fps for any resolution in the list.
*
* @param enabled {@code true} to allow to select high resolution for the use case.
* @return the current Builder.
*/
@NonNull
public Builder setHighResolutionEnabled(boolean enabled) {
mIsHighResolutionEnabled = enabled;
return this;
}
/**
* Builds the {@link ResolutionSelector}.
*
* @return the {@link ResolutionSelector} built with the specified resolution settings.
*/
@NonNull
public ResolutionSelector build() {
return new ResolutionSelector(mPreferredAspectRatio, mPreferredResolution,
mSizeCoordinate, mMaxResolution, mIsHighResolutionEnabled);
}
}
}