1 /*
2  * Copyright 2023 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package androidx.camera.core.resolutionselector;
18 
19 import static androidx.camera.core.resolutionselector.AspectRatioStrategy.RATIO_4_3_FALLBACK_AUTO_STRATEGY;
20 
21 import androidx.annotation.IntDef;
22 import androidx.annotation.RestrictTo;
23 import androidx.camera.core.UseCase;
24 import androidx.camera.core.UseCaseGroup;
25 import androidx.camera.core.ViewPort;
26 
27 import org.jspecify.annotations.NonNull;
28 import org.jspecify.annotations.Nullable;
29 
30 import java.lang.annotation.Retention;
31 import java.lang.annotation.RetentionPolicy;
32 
33 /**
34  * A set of requirements and priorities used to select a resolution for the {@link UseCase}.
35  *
36  * <p>The resolution selection mechanism is determined by the following three steps:
37  * <ol>
38  *     <li> Collect the supported output sizes and add them to the candidate resolution list.
39  *     <li> Filter and sort the candidate resolution list according to the {@link Builder}
40  *     resolution settings.
41  *     <li> Consider all the resolution selector settings of bound {@link UseCase}s to find the
42  *     resolution that best suits each {@link UseCase}.
43  * </ol>
44  *
45  * <p>For the first step, all supported resolution output sizes are added to the candidate
46  * resolution list as the starting point.
47  *
48  * <p>ResolutionSelector provides the following function for applications to adjust the candidate
49  * resolution settings.
50  * <ul>
51  *     <li> {@link Builder#setAllowedResolutionMode(int)}
52  * </ul>
53  *
54  * <p>For the second step, ResolutionSelector provides the following three functions for
55  * applications to determine which resolution should be selected with higher priority.
56  * <ul>
57  *     <li> {@link Builder#setAspectRatioStrategy(AspectRatioStrategy)}
58  *     <li> {@link Builder#setResolutionStrategy(ResolutionStrategy)}
59  *     <li> {@link Builder#setResolutionFilter(ResolutionFilter)}
60  * </ul>
61  *
62  * <p>CameraX sorts the collected sizes according to the specified aspect ratio and resolution
63  * strategies. The aspect ratio strategy has precedence over the resolution strategy for sorting
64  * the resolution candidate list. If applications specify a custom resolution filter, CameraX
65  * passes the resulting sizes list, sorted by the specified aspect ratio and resolution
66  * strategies, to the resolution filter to get the final desired list.
67  *
68  * <p>Different types of {@link UseCase}s might have their own default settings. You can see the
69  * {@link UseCase} builders’ {@code setResolutionSelector()} function to know the details for each
70  * type of {@link UseCase}.
71  *
72  * <p>In the third step, CameraX selects the final resolution for the {@link UseCase} based on the
73  * camera device's hardware level, capabilities, and the bound {@link UseCase} combination.
74  * Applications can check which resolution is finally selected by using the {@link UseCase}'s
75  * {@code getResolutionInfo()} function.
76  *
77  * <p>Note that a ResolutionSelector with more restricted settings may result in that no
78  * resolution can be selected to use. Applications will receive {@link IllegalArgumentException}
79  * when binding the {@link UseCase}s with such kind of ResolutionSelector. Applications can
80  * specify the {@link AspectRatioStrategy} and {@link ResolutionStrategy} with proper fallback
81  * rules to avoid the {@link IllegalArgumentException} or try-catch it and show a proper message
82  * to the end users.
83  *
84  * <p>When creating a ResolutionSelector instance, the
85  * {@link AspectRatioStrategy#RATIO_4_3_FALLBACK_AUTO_STRATEGY} will be the default
86  * {@link AspectRatioStrategy} if it is not set.
87  * {@link ResolutionSelector#PREFER_CAPTURE_RATE_OVER_HIGHER_RESOLUTION} is the default allowed
88  * resolution mode. However, if neither the {@link ResolutionStrategy} nor the
89  * {@link ResolutionFilter} are set, there will be no default value specified.
90  */
91 public final class ResolutionSelector {
92     /**
93      * This mode allows CameraX to select the normal output sizes on the camera device.
94      *
95      * <p>The available resolutions for this mode are obtained from the
96      * {@link android.hardware.camera2.params.StreamConfigurationMap#getOutputSizes(int)} method
97      * from the stream configuration map obtained with the
98      * {@link android.hardware.camera2.CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP}
99      * camera characteristics.
100      */
101     public static final int PREFER_CAPTURE_RATE_OVER_HIGHER_RESOLUTION = 0;
102     /**
103      * This mode allows CameraX to select the output sizes which might result in slower capture
104      * times.
105      *
106      * <p>The available resolutions for this mode are obtained from the
107      * {@link android.hardware.camera2.params.StreamConfigurationMap#getOutputSizes(int)} and
108      * {@link android.hardware.camera2.params.StreamConfigurationMap#getHighResolutionOutputSizes(int)}
109      * methods from the stream configuration map obtained with the
110      * {@link android.hardware.camera2.CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP}
111      * camera characteristics. However, please note that using a resolution obtained from the
112      * {@link android.hardware.camera2.params.StreamConfigurationMap#getHighResolutionOutputSizes(int)}
113      * may result in slower capture times. Please see the javadoc of
114      * {@link android.hardware.camera2.params.StreamConfigurationMap#getHighResolutionOutputSizes(int)}
115      * for more details.
116      *
117      * <p>Since Android 12, some devices might support a maximum resolution sensor pixel mode,
118      * which allows them to capture additional ultra high resolutions retrieved from
119      * {@link android.hardware.camera2.CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP_MAXIMUM_RESOLUTION}
120      * . This mode does not allow applications to select those ultra high resolutions.
121      */
122     public static final int PREFER_HIGHER_RESOLUTION_OVER_CAPTURE_RATE = 1;
123 
124     @IntDef({PREFER_CAPTURE_RATE_OVER_HIGHER_RESOLUTION,
125             PREFER_HIGHER_RESOLUTION_OVER_CAPTURE_RATE})
126     @Retention(RetentionPolicy.SOURCE)
127     @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
128     public @interface AllowedResolutionMode {
129     }
130     private final @NonNull AspectRatioStrategy mAspectRatioStrategy;
131     private final @Nullable ResolutionStrategy mResolutionStrategy;
132     private final @Nullable ResolutionFilter mResolutionFilter;
133     @AllowedResolutionMode
134     private final int mAllowedResolutionMode;
135 
ResolutionSelector( @onNull AspectRatioStrategy aspectRatioStrategy, @Nullable ResolutionStrategy resolutionStrategy, @Nullable ResolutionFilter resolutionFilter, @AllowedResolutionMode int allowedResolutionMode)136     ResolutionSelector(
137             @NonNull AspectRatioStrategy aspectRatioStrategy,
138             @Nullable ResolutionStrategy resolutionStrategy,
139             @Nullable ResolutionFilter resolutionFilter,
140             @AllowedResolutionMode int allowedResolutionMode) {
141         mAspectRatioStrategy = aspectRatioStrategy;
142         mResolutionStrategy = resolutionStrategy;
143         mResolutionFilter = resolutionFilter;
144         mAllowedResolutionMode = allowedResolutionMode;
145     }
146 
147     /**
148      * Returns the specified {@link AspectRatioStrategy}, or
149      * {@link AspectRatioStrategy#RATIO_4_3_FALLBACK_AUTO_STRATEGY} if none is specified when
150      * creating the ResolutionSelector.
151      */
getAspectRatioStrategy()152     public @NonNull AspectRatioStrategy getAspectRatioStrategy() {
153         return mAspectRatioStrategy;
154     }
155 
156     /**
157      * Returns the specified {@link ResolutionStrategy}, or null if not specified.
158      */
getResolutionStrategy()159     public @Nullable ResolutionStrategy getResolutionStrategy() {
160         return mResolutionStrategy;
161     }
162 
163     /**
164      * Returns the specified {@link ResolutionFilter} implementation, or null if not specified.
165      */
getResolutionFilter()166     public @Nullable ResolutionFilter getResolutionFilter() {
167         return mResolutionFilter;
168     }
169 
170     /**
171      * Returns the specified allowed resolution mode.
172      */
173     @AllowedResolutionMode
getAllowedResolutionMode()174     public int getAllowedResolutionMode() {
175         return mAllowedResolutionMode;
176     }
177 
178     /**
179      * Builder for a {@link ResolutionSelector}.
180      */
181     public static final class Builder {
182         private @Nullable AspectRatioStrategy mAspectRatioStrategy =
183                 RATIO_4_3_FALLBACK_AUTO_STRATEGY;
184         private @Nullable ResolutionStrategy mResolutionStrategy = null;
185         private @Nullable ResolutionFilter mResolutionFilter = null;
186         @AllowedResolutionMode
187         private int mAllowedResolutionMode = PREFER_CAPTURE_RATE_OVER_HIGHER_RESOLUTION;
188 
189         /**
190          * Creates a Builder instance.
191          */
Builder()192         public Builder() {
193         }
194 
Builder(@onNull ResolutionSelector resolutionSelector)195         private Builder(@NonNull ResolutionSelector resolutionSelector) {
196             mAspectRatioStrategy = resolutionSelector.getAspectRatioStrategy();
197             mResolutionStrategy = resolutionSelector.getResolutionStrategy();
198             mResolutionFilter = resolutionSelector.getResolutionFilter();
199             mAllowedResolutionMode = resolutionSelector.getAllowedResolutionMode();
200         }
201 
202         /**
203          * Creates a Builder from an existing resolution selector.
204          */
205         @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
fromResolutionSelector( @onNull ResolutionSelector resolutionSelector)206         public static @NonNull Builder fromResolutionSelector(
207                 @NonNull ResolutionSelector resolutionSelector) {
208             return new Builder(resolutionSelector);
209         }
210 
211         /**
212          * Sets the aspect ratio selection strategy for the {@link UseCase}. The aspect ratio
213          * selection strategy determines how the {@link UseCase} will choose the aspect ratio of
214          * the captured image.
215          *
216          * <p>If the aspect ratio strategy is not specified,
217          * {@link AspectRatioStrategy#RATIO_4_3_FALLBACK_AUTO_STRATEGY} will be used as the default.
218          *
219          * <p>{@link UseCase}s can be bound by a {@link UseCaseGroup} with a {@link ViewPort}
220          * setting. If a {@link ViewPort} is set, it is recommended that the {@link ViewPort} and
221          * the bound {@link UseCase}s should have matching aspect ratio settings. Otherwise, the
222          * output crop rectangles may be double-cropped from the full camera sensor field of view.
223          * See {@link ViewPort.Builder} for details.
224          *
225          * <p>CameraX only supports the common 4:3 and 16:9 aspect ratio settings. Some devices may
226          * offer additional output sizes. To access these, you'll need to create a
227          * {@link ResolutionSelector} with a {@link ResolutionFilter} to find and select those
228          * specific sizes.
229          */
setAspectRatioStrategy( @onNull AspectRatioStrategy aspectRatioStrategy)230         public @NonNull Builder setAspectRatioStrategy(
231                 @NonNull AspectRatioStrategy aspectRatioStrategy) {
232             mAspectRatioStrategy = aspectRatioStrategy;
233             return this;
234         }
235 
236         /**
237          * Sets the resolution selection strategy for the {@link UseCase}. The resolution selection
238          * strategy determines how the {@link UseCase} will choose the resolution of the captured
239          * image.
240          *
241          * <p>Note: {@link ResolutionStrategy} works in conjunction with
242          * {@link AspectRatioStrategy} and the default {@link AspectRatioStrategy} is
243          * {@link AspectRatioStrategy#RATIO_4_3_FALLBACK_AUTO_STRATEGY}. Ensure you set a
244          * corresponding {@link AspectRatioStrategy} alongside your {@link ResolutionStrategy}.
245          * For example, if your {@link ResolutionStrategy} uses a bound size of {@code 1920x1080}
246          * and a 16:9 aspect ratio is preferred, set
247          * {@link AspectRatioStrategy#RATIO_16_9_FALLBACK_AUTO_STRATEGY} when building the
248          * {@link ResolutionSelector}.
249          *
250          * <p>CameraX only supports the common 4:3 and 16:9 aspect ratio settings. To select
251          * resolution of other aspect ratios, you'll need to create a {@link ResolutionSelector}
252          * with a {@link ResolutionFilter} to find and select those specific sizes.
253          */
setResolutionStrategy( @onNull ResolutionStrategy resolutionStrategy)254         public @NonNull Builder setResolutionStrategy(
255                 @NonNull ResolutionStrategy resolutionStrategy) {
256             mResolutionStrategy = resolutionStrategy;
257             return this;
258         }
259 
260         /**
261          * Sets the resolution filter to output the final desired sizes list. The resolution
262          * filter will filter out unsuitable sizes and sort the resolution list in the preferred
263          * order. The preferred order is the order in which the resolutions should be tried first.
264          */
setResolutionFilter(@onNull ResolutionFilter resolutionFilter)265         public @NonNull Builder setResolutionFilter(@NonNull ResolutionFilter resolutionFilter) {
266             mResolutionFilter = resolutionFilter;
267             return this;
268         }
269 
270         /**
271          * Sets the allowed resolution mode.
272          *
273          * <p>If not specified, the default setting is
274          * {@link ResolutionSelector#PREFER_CAPTURE_RATE_OVER_HIGHER_RESOLUTION}.
275          */
setAllowedResolutionMode(@llowedResolutionMode int mode)276         public @NonNull Builder setAllowedResolutionMode(@AllowedResolutionMode int mode) {
277             mAllowedResolutionMode = mode;
278             return this;
279         }
280 
281         /**
282          * Builds the resolution selector. This will create a resolution selector that can be
283          * used to select the desired resolution for the captured image.
284          */
build()285         public @NonNull ResolutionSelector build() {
286             return new ResolutionSelector(mAspectRatioStrategy, mResolutionStrategy,
287                     mResolutionFilter, mAllowedResolutionMode);
288         }
289     }
290 }
291