blob: 20d9c30bb4ccef2701cef7fece1c69d75dc7989f [file] [log] [blame]
Emilian Peev423cbd72018-11-10 18:37:45 +00001/*
2 * Copyright (C) 2018 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
17package android.hardware.camera2.params;
18
Emilian Peev423cbd72018-11-10 18:37:45 +000019import static android.hardware.camera2.params.StreamConfigurationMap.checkArgumentFormat;
20
Jayant Chowdharya24f94b2020-01-14 11:37:46 -080021import static com.android.internal.util.Preconditions.*;
22
Emilian Peev423cbd72018-11-10 18:37:45 +000023import android.annotation.NonNull;
Emilian Peev0081bcf2019-02-26 14:55:58 -080024import android.annotation.Nullable;
Emilian Peev423cbd72018-11-10 18:37:45 +000025import android.graphics.ImageFormat;
Emilian Peev0081bcf2019-02-26 14:55:58 -080026import android.graphics.ImageFormat.Format;
Emilian Peev423cbd72018-11-10 18:37:45 +000027import android.hardware.camera2.CameraCharacteristics;
Emilian Peev423cbd72018-11-10 18:37:45 +000028import android.hardware.camera2.CameraDevice;
Shuzhen Wangaae38962019-05-30 10:11:47 -070029import android.hardware.camera2.CameraManager;
Emilian Peev423cbd72018-11-10 18:37:45 +000030import android.hardware.camera2.CameraMetadata;
31import android.hardware.camera2.params.StreamConfigurationMap;
32import android.hardware.camera2.utils.HashCodeHelpers;
Emilian Peev423cbd72018-11-10 18:37:45 +000033import android.media.CamcorderProfile;
Emilian Peev423cbd72018-11-10 18:37:45 +000034import android.util.Log;
35import android.util.Pair;
Jayant Chowdharya24f94b2020-01-14 11:37:46 -080036import android.util.Size;
Emilian Peev423cbd72018-11-10 18:37:45 +000037
Emilian Peev423cbd72018-11-10 18:37:45 +000038import java.util.ArrayList;
Jayant Chowdharya24f94b2020-01-14 11:37:46 -080039import java.util.Arrays;
Emilian Peev423cbd72018-11-10 18:37:45 +000040import java.util.Collections;
41import java.util.Comparator;
42import java.util.HashMap;
43import java.util.List;
44
45/**
46 * Immutable class to store the available mandatory stream combination.
47 *
Emilian Peev0081bcf2019-02-26 14:55:58 -080048 * <p>A mandatory stream combination refers to a specific entry in the documented sets of
49 * required stream {@link CameraDevice#createCaptureSession combinations}.
50 * These combinations of streams are required to be supported by the camera device.
Emilian Peev423cbd72018-11-10 18:37:45 +000051 *
52 * <p>The list of stream combinations is available by invoking
53 * {@link CameraCharacteristics#get} and passing key
54 * {@link android.hardware.camera2.CameraCharacteristics#SCALER_MANDATORY_STREAM_COMBINATIONS}.</p>
55 */
56public final class MandatoryStreamCombination {
57 private static final String TAG = "MandatoryStreamCombination";
58 /**
59 * Immutable class to store available mandatory stream information.
60 */
61 public static final class MandatoryStreamInformation {
62 private final int mFormat;
63 private final ArrayList<Size> mAvailableSizes = new ArrayList<Size> ();
Emilian Peev275da0b2018-11-15 18:20:24 +000064 private final boolean mIsInput;
Emilian Peev423cbd72018-11-10 18:37:45 +000065
66 /**
67 * Create a new {@link MandatoryStreamInformation}.
68 *
Emilian Peev275da0b2018-11-15 18:20:24 +000069 @param availableSizes List of possible stream sizes.
Emilian Peev423cbd72018-11-10 18:37:45 +000070 * @param format Image format.
71 *
72 * @throws IllegalArgumentException
73 * if sizes is empty or if the format was not user-defined in
74 * ImageFormat/PixelFormat.
75 * @hide
76 */
77 public MandatoryStreamInformation(@NonNull List<Size> availableSizes, int format) {
Emilian Peev275da0b2018-11-15 18:20:24 +000078 this(availableSizes, format, /*isInput*/false);
79 }
80
81 /**
82 * Create a new {@link MandatoryStreamInformation}.
83 *
84 @param availableSizes List of possible stream sizes.
85 * @param format Image format.
86 * @param isInput Flag indicating whether this stream is input.
87 *
88 * @throws IllegalArgumentException
89 * if sizes is empty or if the format was not user-defined in
90 * ImageFormat/PixelFormat.
91 * @hide
92 */
Emilian Peev0081bcf2019-02-26 14:55:58 -080093 public MandatoryStreamInformation(@NonNull List<Size> availableSizes, @Format int format,
Emilian Peev275da0b2018-11-15 18:20:24 +000094 boolean isInput) {
Emilian Peev423cbd72018-11-10 18:37:45 +000095 if (availableSizes.isEmpty()) {
96 throw new IllegalArgumentException("No available sizes");
97 }
98 mAvailableSizes.addAll(availableSizes);
99 mFormat = checkArgumentFormat(format);
Emilian Peev275da0b2018-11-15 18:20:24 +0000100 mIsInput = isInput;
101 }
102
103 /**
104 * Confirms whether or not this is an input stream.
105 * @return true in case the stream is input, false otherwise.
106 */
107 public boolean isInput() {
108 return mIsInput;
Emilian Peev423cbd72018-11-10 18:37:45 +0000109 }
110
111 /**
112 * Return the list of available sizes for this mandatory stream.
113 *
114 * <p>Per documented {@link CameraDevice#createCaptureSession guideline} the largest
115 * resolution in the result will be tested and guaranteed to work. If clients want to use
116 * smaller sizes, then the resulting
117 * {@link android.hardware.camera2.params.SessionConfiguration session configuration} can
118 * be tested either by calling {@link CameraDevice#createCaptureSession} or
119 * {@link CameraDevice#isSessionConfigurationSupported}.
120 *
121 * @return non-modifiable ascending list of available sizes.
122 */
Emilian Peev0081bcf2019-02-26 14:55:58 -0800123 public @NonNull List<Size> getAvailableSizes() {
Emilian Peev423cbd72018-11-10 18:37:45 +0000124 return Collections.unmodifiableList(mAvailableSizes);
125 }
126
127 /**
128 * Retrieve the mandatory stream {@code format}.
129 *
130 * @return integer format.
131 */
Emilian Peev0081bcf2019-02-26 14:55:58 -0800132 public @Format int getFormat() {
Emilian Peev423cbd72018-11-10 18:37:45 +0000133 return mFormat;
134 }
135
136 /**
137 * Check if this {@link MandatoryStreamInformation} is equal to another
138 * {@link MandatoryStreamInformation}.
139 *
140 * <p>Two vectors are only equal if and only if each of the respective elements is
141 * equal.</p>
142 *
143 * @return {@code true} if the objects were equal, {@code false} otherwise
144 */
145 @Override
146 public boolean equals(final Object obj) {
147 if (obj == null) {
148 return false;
149 }
150 if (this == obj) {
151 return true;
152 }
153 if (obj instanceof MandatoryStreamInformation) {
154 final MandatoryStreamInformation other = (MandatoryStreamInformation) obj;
Emilian Peev275da0b2018-11-15 18:20:24 +0000155 if ((mFormat != other.mFormat) || (mIsInput != other.mIsInput) ||
Emilian Peev423cbd72018-11-10 18:37:45 +0000156 (mAvailableSizes.size() != other.mAvailableSizes.size())) {
157 return false;
158 }
159
160 return mAvailableSizes.equals(other.mAvailableSizes);
161 }
162
163 return false;
164 }
165
166 /**
167 * {@inheritDoc}
168 */
169 @Override
170 public int hashCode() {
Emilian Peev275da0b2018-11-15 18:20:24 +0000171 return HashCodeHelpers.hashCode(mFormat, Boolean.hashCode(mIsInput),
172 mAvailableSizes.hashCode());
Emilian Peev423cbd72018-11-10 18:37:45 +0000173 }
174 }
175
Emilian Peev275da0b2018-11-15 18:20:24 +0000176 private final String mDescription;
177 private final boolean mIsReprocessable;
Emilian Peev423cbd72018-11-10 18:37:45 +0000178 private final ArrayList<MandatoryStreamInformation> mStreamsInformation =
179 new ArrayList<MandatoryStreamInformation>();
180 /**
181 * Create a new {@link MandatoryStreamCombination}.
182 *
183 * @param streamsInformation list of available streams in the stream combination.
184 * @param description Summary of the stream combination use case.
Emilian Peev275da0b2018-11-15 18:20:24 +0000185 * @param isReprocessable Flag whether the mandatory stream combination is reprocessable.
Emilian Peev423cbd72018-11-10 18:37:45 +0000186 *
187 * @throws IllegalArgumentException
188 * if stream information is empty
189 * @hide
190 */
191 public MandatoryStreamCombination(@NonNull List<MandatoryStreamInformation> streamsInformation,
Emilian Peev0081bcf2019-02-26 14:55:58 -0800192 @NonNull String description, boolean isReprocessable) {
Emilian Peev423cbd72018-11-10 18:37:45 +0000193 if (streamsInformation.isEmpty()) {
194 throw new IllegalArgumentException("Empty stream information");
195 }
196 mStreamsInformation.addAll(streamsInformation);
Emilian Peev275da0b2018-11-15 18:20:24 +0000197 mDescription = description;
198 mIsReprocessable = isReprocessable;
Emilian Peev423cbd72018-11-10 18:37:45 +0000199 }
Emilian Peev423cbd72018-11-10 18:37:45 +0000200 /**
201 * Get the mandatory stream combination description.
202 *
Emilian Peev0081bcf2019-02-26 14:55:58 -0800203 * @return CharSequence with the mandatory combination description.
Emilian Peev423cbd72018-11-10 18:37:45 +0000204 */
Emilian Peev0081bcf2019-02-26 14:55:58 -0800205 public @NonNull CharSequence getDescription() {
Emilian Peev275da0b2018-11-15 18:20:24 +0000206 return mDescription;
207 }
208
209 /**
Emilian Peev0081bcf2019-02-26 14:55:58 -0800210 * Indicates whether the mandatory stream combination is reprocessable. Reprocessable is defined
211 * as a stream combination that contains one input stream
212 * ({@link MandatoryStreamInformation#isInput} return true).
Emilian Peev275da0b2018-11-15 18:20:24 +0000213 *
214 * @return {@code true} in case the mandatory stream combination contains an input,
215 * {@code false} otherwise.
216 */
217 public boolean isReprocessable() {
218 return mIsReprocessable;
Emilian Peev423cbd72018-11-10 18:37:45 +0000219 }
220
221 /**
222 * Get information about each stream in the mandatory combination.
223 *
224 * @return Non-modifiable list of stream information.
225 *
226 */
Emilian Peev0081bcf2019-02-26 14:55:58 -0800227 public @NonNull List<MandatoryStreamInformation> getStreamsInformation() {
Emilian Peev423cbd72018-11-10 18:37:45 +0000228 return Collections.unmodifiableList(mStreamsInformation);
229 }
230
231 /**
232 * Check if this {@link MandatoryStreamCombination} is equal to another
233 * {@link MandatoryStreamCombination}.
234 *
235 * <p>Two vectors are only equal if and only if each of the respective elements is equal.</p>
236 *
237 * @return {@code true} if the objects were equal, {@code false} otherwise
238 */
239 @Override
240 public boolean equals(final Object obj) {
241 if (obj == null) {
242 return false;
243 }
244 if (this == obj) {
245 return true;
246 }
247 if (obj instanceof MandatoryStreamCombination) {
248 final MandatoryStreamCombination other = (MandatoryStreamCombination) obj;
Emilian Peev275da0b2018-11-15 18:20:24 +0000249 if ((mDescription != other.mDescription) ||
250 (mIsReprocessable != other.mIsReprocessable) ||
Emilian Peev423cbd72018-11-10 18:37:45 +0000251 (mStreamsInformation.size() != other.mStreamsInformation.size())) {
252 return false;
253 }
254
Emilian Peev275da0b2018-11-15 18:20:24 +0000255 return mStreamsInformation.equals(other.mStreamsInformation);
Emilian Peev423cbd72018-11-10 18:37:45 +0000256 }
257
258 return false;
259 }
260
261 /**
262 * {@inheritDoc}
263 */
264 @Override
265 public int hashCode() {
Emilian Peev275da0b2018-11-15 18:20:24 +0000266 return HashCodeHelpers.hashCode(Boolean.hashCode(mIsReprocessable), mDescription.hashCode(),
267 mStreamsInformation.hashCode());
Emilian Peev423cbd72018-11-10 18:37:45 +0000268 }
269
Jayant Chowdharyac404c12020-02-11 20:37:22 -0800270 private static enum SizeThreshold { VGA, PREVIEW, RECORD, MAXIMUM, s720p, s1440p }
Emilian Peev275da0b2018-11-15 18:20:24 +0000271 private static enum ReprocessType { NONE, PRIVATE, YUV }
Emilian Peev423cbd72018-11-10 18:37:45 +0000272 private static final class StreamTemplate {
273 public int mFormat;
274 public SizeThreshold mSizeThreshold;
Emilian Peev275da0b2018-11-15 18:20:24 +0000275 public boolean mIsInput;
Emilian Peev423cbd72018-11-10 18:37:45 +0000276 public StreamTemplate(int format, SizeThreshold sizeThreshold) {
Emilian Peev275da0b2018-11-15 18:20:24 +0000277 this(format, sizeThreshold, /*isInput*/false);
278 }
279
Emilian Peev0081bcf2019-02-26 14:55:58 -0800280 public StreamTemplate(@Format int format, @NonNull SizeThreshold sizeThreshold,
281 boolean isInput) {
Emilian Peev423cbd72018-11-10 18:37:45 +0000282 mFormat = format;
283 mSizeThreshold = sizeThreshold;
Emilian Peev275da0b2018-11-15 18:20:24 +0000284 mIsInput = isInput;
Emilian Peev423cbd72018-11-10 18:37:45 +0000285 }
286 }
287
288 private static final class StreamCombinationTemplate {
289 public StreamTemplate[] mStreamTemplates;
290 public String mDescription;
Emilian Peev275da0b2018-11-15 18:20:24 +0000291 public ReprocessType mReprocessType;
Emilian Peev423cbd72018-11-10 18:37:45 +0000292
Emilian Peev0081bcf2019-02-26 14:55:58 -0800293 public StreamCombinationTemplate(@NonNull StreamTemplate[] streamTemplates,
294 @NonNull String description) {
Emilian Peev275da0b2018-11-15 18:20:24 +0000295 this(streamTemplates, description, /*reprocessType*/ReprocessType.NONE);
296 }
297
Emilian Peev0081bcf2019-02-26 14:55:58 -0800298 public StreamCombinationTemplate(@NonNull StreamTemplate[] streamTemplates,
299 @NonNull String description,
Emilian Peev275da0b2018-11-15 18:20:24 +0000300 ReprocessType reprocessType) {
Emilian Peev423cbd72018-11-10 18:37:45 +0000301 mStreamTemplates = streamTemplates;
Emilian Peev275da0b2018-11-15 18:20:24 +0000302 mReprocessType = reprocessType;
Emilian Peev423cbd72018-11-10 18:37:45 +0000303 mDescription = description;
304 }
305 }
306
307 private static StreamCombinationTemplate sLegacyCombinations[] = {
308 new StreamCombinationTemplate(new StreamTemplate [] {
309 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.MAXIMUM) },
310 "Simple preview, GPU video processing, or no-preview video recording"),
311 new StreamCombinationTemplate(new StreamTemplate [] {
312 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM) },
313 "No-viewfinder still image capture"),
314 new StreamCombinationTemplate(new StreamTemplate [] {
315 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.MAXIMUM) },
316 "In-application video/image processing"),
317 new StreamCombinationTemplate(new StreamTemplate [] {
318 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
319 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM) },
320 "Standard still imaging"),
321 new StreamCombinationTemplate(new StreamTemplate [] {
322 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
323 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM) },
324 "In-app processing plus still capture"),
325 new StreamCombinationTemplate(new StreamTemplate [] {
326 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
327 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW) },
328 "Standard recording"),
329 new StreamCombinationTemplate(new StreamTemplate [] {
330 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
331 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW) },
332 "Preview plus in-app processing"),
333 new StreamCombinationTemplate(new StreamTemplate [] {
334 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
335 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
336 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM) },
337 "Still capture plus in-app processing")
338 };
339
340 private static StreamCombinationTemplate sLimitedCombinations[] = {
341 new StreamCombinationTemplate(new StreamTemplate [] {
342 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
343 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.RECORD)},
344 "High-resolution video recording with preview"),
345 new StreamCombinationTemplate(new StreamTemplate [] {
346 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
347 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.RECORD)},
348 "High-resolution in-app video processing with preview"),
349 new StreamCombinationTemplate(new StreamTemplate [] {
350 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
351 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.RECORD) },
352 "Two-input in-app video processing"),
353 new StreamCombinationTemplate(new StreamTemplate [] {
354 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
355 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.RECORD),
356 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.RECORD) },
357 "High-resolution recording with video snapshot"),
358 new StreamCombinationTemplate(new StreamTemplate [] {
359 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
360 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.RECORD),
361 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.RECORD) },
362 "High-resolution in-app processing with video snapshot"),
363 new StreamCombinationTemplate(new StreamTemplate [] {
364 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
365 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
366 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM) },
367 "Two-input in-app processing with still capture")
368 };
369
370 private static StreamCombinationTemplate sBurstCombinations[] = {
371 new StreamCombinationTemplate(new StreamTemplate [] {
372 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
373 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.MAXIMUM) },
374 "Maximum-resolution GPU processing with preview"),
375 new StreamCombinationTemplate(new StreamTemplate [] {
376 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
377 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.MAXIMUM) },
378 "Maximum-resolution in-app processing with preview"),
379 new StreamCombinationTemplate(new StreamTemplate [] {
380 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
381 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.MAXIMUM) },
382 "Maximum-resolution two-input in-app processsing")
383 };
384
385 private static StreamCombinationTemplate sFullCombinations[] = {
386 new StreamCombinationTemplate(new StreamTemplate [] {
387 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.MAXIMUM),
388 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.MAXIMUM) },
389 "Maximum-resolution GPU processing with preview"),
390 new StreamCombinationTemplate(new StreamTemplate [] {
391 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.MAXIMUM),
392 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.MAXIMUM) },
393 "Maximum-resolution in-app processing with preview"),
394 new StreamCombinationTemplate(new StreamTemplate [] {
395 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.MAXIMUM),
396 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.MAXIMUM) },
397 "Maximum-resolution two-input in-app processsing"),
398 new StreamCombinationTemplate(new StreamTemplate [] {
399 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
400 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
401 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM) },
402 "Video recording with maximum-size video snapshot"),
403 new StreamCombinationTemplate(new StreamTemplate [] {
404 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.VGA),
405 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
406 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.MAXIMUM) },
407 "Standard video recording plus maximum-resolution in-app processing"),
408 new StreamCombinationTemplate(new StreamTemplate [] {
409 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.VGA),
410 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
411 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.MAXIMUM) },
412 "Preview plus two-input maximum-resolution in-app processing")
413 };
414
415 private static StreamCombinationTemplate sRawCombinations[] = {
416 new StreamCombinationTemplate(new StreamTemplate [] {
417 new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.MAXIMUM) },
418 "No-preview DNG capture"),
419 new StreamCombinationTemplate(new StreamTemplate [] {
420 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
421 new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.MAXIMUM) },
422 "Standard DNG capture"),
423 new StreamCombinationTemplate(new StreamTemplate [] {
424 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
425 new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.MAXIMUM) },
426 "In-app processing plus DNG capture"),
427 new StreamCombinationTemplate(new StreamTemplate [] {
428 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
429 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
430 new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.MAXIMUM) },
431 "Video recording with DNG capture"),
432 new StreamCombinationTemplate(new StreamTemplate [] {
433 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
434 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
435 new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.MAXIMUM) },
436 "Preview with in-app processing and DNG capture"),
437 new StreamCombinationTemplate(new StreamTemplate [] {
438 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
439 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
440 new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.MAXIMUM) },
441 "Two-input in-app processing plus DNG capture"),
442 new StreamCombinationTemplate(new StreamTemplate [] {
443 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
444 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM),
445 new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.MAXIMUM) },
446 "Still capture with simultaneous JPEG and DNG"),
447 new StreamCombinationTemplate(new StreamTemplate [] {
448 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
449 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM),
450 new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.MAXIMUM) },
451 "In-app processing with simultaneous JPEG and DNG")
452 };
453
454 private static StreamCombinationTemplate sLevel3Combinations[] = {
455 new StreamCombinationTemplate(new StreamTemplate [] {
456 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
457 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.VGA),
458 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.MAXIMUM),
459 new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.MAXIMUM) },
460 "In-app viewfinder analysis with dynamic selection of output format"),
461 new StreamCombinationTemplate(new StreamTemplate [] {
462 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
463 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.VGA),
464 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM),
465 new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.MAXIMUM) },
466 "In-app viewfinder analysis with dynamic selection of output format")
467 };
468
Emilian Peev275da0b2018-11-15 18:20:24 +0000469 private static StreamCombinationTemplate sLimitedPrivateReprocCombinations[] = {
470 new StreamCombinationTemplate(new StreamTemplate [] {
471 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM) },
472 "No-viewfinder still image reprocessing",
473 /*reprocessType*/ ReprocessType.PRIVATE),
474 new StreamCombinationTemplate(new StreamTemplate [] {
475 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
476 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM) },
477 "ZSL(Zero-Shutter-Lag) still imaging",
478 /*reprocessType*/ ReprocessType.PRIVATE),
479 new StreamCombinationTemplate(new StreamTemplate [] {
480 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
481 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM) },
482 "ZSL still and in-app processing imaging",
483 /*reprocessType*/ ReprocessType.PRIVATE),
484 new StreamCombinationTemplate(new StreamTemplate [] {
485 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
486 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
487 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM) },
488 "ZSL in-app processing with still capture",
489 /*reprocessType*/ ReprocessType.PRIVATE),
490 };
491
492 private static StreamCombinationTemplate sLimitedYUVReprocCombinations[] = {
493 new StreamCombinationTemplate(new StreamTemplate [] {
494 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM) },
495 "No-viewfinder still image reprocessing",
496 /*reprocessType*/ ReprocessType.YUV),
497 new StreamCombinationTemplate(new StreamTemplate [] {
498 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
499 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM) },
500 "ZSL(Zero-Shutter-Lag) still imaging",
501 /*reprocessType*/ ReprocessType.YUV),
502 new StreamCombinationTemplate(new StreamTemplate [] {
503 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
504 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM) },
505 "ZSL still and in-app processing imaging",
506 /*reprocessType*/ ReprocessType.YUV),
507 new StreamCombinationTemplate(new StreamTemplate [] {
508 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
509 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
510 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM) },
511 "ZSL in-app processing with still capture",
512 /*reprocessType*/ ReprocessType.YUV),
513 };
514
515 private static StreamCombinationTemplate sFullPrivateReprocCombinations[] = {
516 new StreamCombinationTemplate(new StreamTemplate [] {
517 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
518 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.RECORD) },
519 "High-resolution ZSL in-app video processing with regular preview",
520 /*reprocessType*/ ReprocessType.PRIVATE),
521 new StreamCombinationTemplate(new StreamTemplate [] {
522 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
523 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.MAXIMUM) },
524 "Maximum-resolution ZSL in-app processing with regular preview",
525 /*reprocessType*/ ReprocessType.PRIVATE),
526 new StreamCombinationTemplate(new StreamTemplate [] {
527 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
528 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.MAXIMUM) },
529 "Maximum-resolution two-input ZSL in-app processing",
530 /*reprocessType*/ ReprocessType.PRIVATE),
531 new StreamCombinationTemplate(new StreamTemplate [] {
532 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
533 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
534 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM) },
535 "ZSL still capture and in-app processing",
536 /*reprocessType*/ ReprocessType.PRIVATE),
537 };
538
539 private static StreamCombinationTemplate sFullYUVReprocCombinations[] = {
540 new StreamCombinationTemplate(new StreamTemplate [] {
541 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW) },
542 "Maximum-resolution multi-frame image fusion in-app processing with regular "
543 + "preview",
544 /*reprocessType*/ ReprocessType.YUV),
545 new StreamCombinationTemplate(new StreamTemplate [] {
546 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW) },
547 "Maximum-resolution multi-frame image fusion two-input in-app processing",
548 /*reprocessType*/ ReprocessType.YUV),
549 new StreamCombinationTemplate(new StreamTemplate [] {
550 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
551 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.RECORD) },
552 "High-resolution ZSL in-app video processing with regular preview",
553 /*reprocessType*/ ReprocessType.YUV),
554 new StreamCombinationTemplate(new StreamTemplate [] {
555 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
556 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
557 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM) },
558 "ZSL still capture and in-app processing",
559 /*reprocessType*/ ReprocessType.YUV),
560 };
561
562 private static StreamCombinationTemplate sRAWPrivateReprocCombinations[] = {
563 new StreamCombinationTemplate(new StreamTemplate [] {
564 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
565 new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.MAXIMUM) },
566 "Mutually exclusive ZSL in-app processing and DNG capture",
567 /*reprocessType*/ ReprocessType.PRIVATE),
568 new StreamCombinationTemplate(new StreamTemplate [] {
569 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
570 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
571 new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.MAXIMUM) },
572 "Mutually exclusive ZSL in-app processing and preview with DNG capture",
573 /*reprocessType*/ ReprocessType.PRIVATE),
574 new StreamCombinationTemplate(new StreamTemplate [] {
575 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
576 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
577 new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.MAXIMUM) },
578 "Mutually exclusive ZSL two-input in-app processing and DNG capture",
579 /*reprocessType*/ ReprocessType.PRIVATE),
580 new StreamCombinationTemplate(new StreamTemplate [] {
581 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
582 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM),
583 new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.MAXIMUM) },
584 "Mutually exclusive ZSL still capture and preview with DNG capture",
585 /*reprocessType*/ ReprocessType.PRIVATE),
586 new StreamCombinationTemplate(new StreamTemplate [] {
587 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
588 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM),
589 new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.MAXIMUM) },
590 "Mutually exclusive ZSL in-app processing with still capture and DNG capture",
591 /*reprocessType*/ ReprocessType.PRIVATE),
592 };
593
594 private static StreamCombinationTemplate sRAWYUVReprocCombinations[] = {
595 new StreamCombinationTemplate(new StreamTemplate [] {
596 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
597 new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.MAXIMUM) },
598 "Mutually exclusive ZSL in-app processing and DNG capture",
599 /*reprocessType*/ ReprocessType.YUV),
600 new StreamCombinationTemplate(new StreamTemplate [] {
601 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
602 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
603 new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.MAXIMUM) },
604 "Mutually exclusive ZSL in-app processing and preview with DNG capture",
605 /*reprocessType*/ ReprocessType.YUV),
606 new StreamCombinationTemplate(new StreamTemplate [] {
607 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
608 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
609 new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.MAXIMUM) },
610 "Mutually exclusive ZSL two-input in-app processing and DNG capture",
611 /*reprocessType*/ ReprocessType.YUV),
612 new StreamCombinationTemplate(new StreamTemplate [] {
613 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
614 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM),
615 new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.MAXIMUM) },
616 "Mutually exclusive ZSL still capture and preview with DNG capture",
617 /*reprocessType*/ ReprocessType.YUV),
618 new StreamCombinationTemplate(new StreamTemplate [] {
619 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
620 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM),
621 new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.MAXIMUM) },
622 "Mutually exclusive ZSL in-app processing with still capture and DNG capture",
623 /*reprocessType*/ ReprocessType.YUV),
624 };
625
626 private static StreamCombinationTemplate sLevel3PrivateReprocCombinations[] = {
627 new StreamCombinationTemplate(new StreamTemplate [] {
628 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
629 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.VGA),
630 new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.MAXIMUM),
631 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM) },
632 "In-app viewfinder analysis with ZSL, RAW, and JPEG reprocessing output",
633 /*reprocessType*/ ReprocessType.PRIVATE),
634 };
635
636 private static StreamCombinationTemplate sLevel3YUVReprocCombinations[] = {
637 new StreamCombinationTemplate(new StreamTemplate [] {
638 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
639 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.VGA),
640 new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.MAXIMUM) },
641 "In-app viewfinder analysis with ZSL and RAW",
642 /*reprocessType*/ ReprocessType.YUV),
643 new StreamCombinationTemplate(new StreamTemplate [] {
644 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
645 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.VGA),
646 new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.MAXIMUM),
647 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM) },
648 "In-app viewfinder analysis with ZSL, RAW, and JPEG reprocessing output",
649 /*reprocessType*/ ReprocessType.YUV),
650 };
651
Jayant Chowdharya24f94b2020-01-14 11:37:46 -0800652 private static StreamCombinationTemplate sConcurrentStreamCombinations[] = {
653 new StreamCombinationTemplate(new StreamTemplate [] {
Jayant Chowdharyac404c12020-02-11 20:37:22 -0800654 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.s1440p) },
Jayant Chowdharya24f94b2020-01-14 11:37:46 -0800655 "In-app video / image processing"),
656 new StreamCombinationTemplate(new StreamTemplate [] {
Jayant Chowdharyac404c12020-02-11 20:37:22 -0800657 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.s1440p) },
Jayant Chowdharya24f94b2020-01-14 11:37:46 -0800658 "preview / preview to GPU"),
659 new StreamCombinationTemplate(new StreamTemplate [] {
Jayant Chowdharyac404c12020-02-11 20:37:22 -0800660 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.s1440p) },
661 "No view-finder still image capture"),
662 new StreamCombinationTemplate(new StreamTemplate [] {
Jayant Chowdharya24f94b2020-01-14 11:37:46 -0800663 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.s720p),
Jayant Chowdharyac404c12020-02-11 20:37:22 -0800664 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.s1440p)},
665 "Two-input in app video / image processing"),
666 new StreamCombinationTemplate(new StreamTemplate [] {
667 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.s720p),
668 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.s1440p)},
669 "High resolution video recording with preview"),
670 new StreamCombinationTemplate(new StreamTemplate [] {
671 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.s720p),
672 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.s1440p)},
673 "In-app video / image processing with preview"),
674 new StreamCombinationTemplate(new StreamTemplate [] {
675 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.s720p),
676 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.s1440p)},
Jayant Chowdharya24f94b2020-01-14 11:37:46 -0800677 "In-app video / image processing with preview"),
678 new StreamCombinationTemplate(new StreamTemplate [] {
679 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.s720p),
Jayant Chowdharyac404c12020-02-11 20:37:22 -0800680 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.s1440p)},
681 "Standard stil image capture"),
Jayant Chowdharya24f94b2020-01-14 11:37:46 -0800682 new StreamCombinationTemplate(new StreamTemplate [] {
Jayant Chowdharyac404c12020-02-11 20:37:22 -0800683 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.s720p),
684 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.s1440p)},
685 "Standard still image capture"),
Jayant Chowdharya24f94b2020-01-14 11:37:46 -0800686 };
687
Emilian Peev423cbd72018-11-10 18:37:45 +0000688 /**
689 * Helper builder class to generate a list of available mandatory stream combinations.
690 * @hide
691 */
692 public static final class Builder {
693 private Size mDisplaySize;
694 private List<Integer> mCapabilities;
695 private int mHwLevel, mCameraId;
696 private StreamConfigurationMap mStreamConfigMap;
Shuzhen Wangaae38962019-05-30 10:11:47 -0700697 private boolean mIsHiddenPhysicalCamera;
Emilian Peev423cbd72018-11-10 18:37:45 +0000698
699 private final Size kPreviewSizeBound = new Size(1920, 1088);
700
701 /**
702 * Helper class to be used to generate the available mandatory stream combinations.
703 *
704 * @param cameraId Current camera id.
705 * @param hwLevel The camera HW level as reported by android.info.supportedHardwareLevel.
706 * @param displaySize The device display size.
707 * @param capabilities The camera device capabilities.
708 * @param sm The camera device stream configuration map.
709 */
710 public Builder(int cameraId, int hwLevel, @NonNull Size displaySize,
711 @NonNull List<Integer> capabilities, @NonNull StreamConfigurationMap sm) {
712 mCameraId = cameraId;
713 mDisplaySize = displaySize;
714 mCapabilities = capabilities;
715 mStreamConfigMap = sm;
716 mHwLevel = hwLevel;
Shuzhen Wangaae38962019-05-30 10:11:47 -0700717 mIsHiddenPhysicalCamera =
718 CameraManager.isHiddenPhysicalCamera(Integer.toString(mCameraId));
Emilian Peev423cbd72018-11-10 18:37:45 +0000719 }
720
721 /**
Jayant Chowdharya24f94b2020-01-14 11:37:46 -0800722 * Retrieve a list of all available mandatory concurrent stream combinations.
723 * This method should only be called for devices which are listed in combinations returned
Jayant Chowdhary44bc7d82020-03-10 11:09:57 -0700724 * by CameraManager.getConcurrentCameraIds.
Jayant Chowdharya24f94b2020-01-14 11:37:46 -0800725 *
726 * @return a non-modifiable list of supported mandatory concurrent stream combinations.
727 */
728 public @NonNull List<MandatoryStreamCombination>
729 getAvailableMandatoryConcurrentStreamCombinations() {
730 // Since concurrent streaming support is optional, we mandate these stream
731 // combinations regardless of camera device capabilities.
732 if (!isColorOutputSupported()) {
733 Log.v(TAG, "Device is not backward compatible!");
734 throw new IllegalArgumentException("Camera device which is not BACKWARD_COMPATIBLE"
735 + " cannot have mandatory concurrent streams");
736 }
737 Size size720p = new Size(1280, 720);
Jayant Chowdharyac404c12020-02-11 20:37:22 -0800738 Size size1440p = new Size(1920, 1440);
Jayant Chowdharya24f94b2020-01-14 11:37:46 -0800739
740 ArrayList<MandatoryStreamCombination> availableConcurrentStreamCombinations =
741 new ArrayList<MandatoryStreamCombination>();
742 availableConcurrentStreamCombinations.ensureCapacity(
743 sConcurrentStreamCombinations.length);
744 for (StreamCombinationTemplate combTemplate : sConcurrentStreamCombinations) {
745 ArrayList<MandatoryStreamInformation> streamsInfo =
746 new ArrayList<MandatoryStreamInformation>();
747 streamsInfo.ensureCapacity(combTemplate.mStreamTemplates.length);
748 for (StreamTemplate template : combTemplate.mStreamTemplates) {
749 MandatoryStreamInformation streamInfo;
750 List<Size> sizes = new ArrayList<Size>();
Jayant Chowdharyac404c12020-02-11 20:37:22 -0800751 Size formatSize = null;
752 switch (template.mSizeThreshold) {
753 case s1440p:
754 formatSize = size1440p;
755 break;
756 default:
757 formatSize = size720p;
758 }
Jayant Chowdharya24f94b2020-01-14 11:37:46 -0800759 Size sizeChosen =
Jayant Chowdharyac404c12020-02-11 20:37:22 -0800760 getMinSize(formatSize,
Jayant Chowdharya24f94b2020-01-14 11:37:46 -0800761 getMaxSize(mStreamConfigMap.getOutputSizes(template.mFormat)));
762 sizes.add(sizeChosen);
763 try {
764 streamInfo = new MandatoryStreamInformation(sizes, template.mFormat);
765 } catch (IllegalArgumentException e) {
766 String cause = "No available sizes found for format: " + template.mFormat
767 + " size threshold: " + template.mSizeThreshold + " combination: "
768 + combTemplate.mDescription;
769 throw new RuntimeException(cause, e);
770 }
771 streamsInfo.add(streamInfo);
772 }
773
774 MandatoryStreamCombination streamCombination;
775 try {
776 streamCombination = new MandatoryStreamCombination(streamsInfo,
777 combTemplate.mDescription, /*isReprocess*/false);
778 } catch (IllegalArgumentException e) {
779 String cause = "No stream information for mandatory combination: "
780 + combTemplate.mDescription;
781 throw new RuntimeException(cause, e);
782 }
783 availableConcurrentStreamCombinations.add(streamCombination);
784 }
785 return Collections.unmodifiableList(availableConcurrentStreamCombinations);
786 }
787
788 /**
Emilian Peev423cbd72018-11-10 18:37:45 +0000789 * Retrieve a list of all available mandatory stream combinations.
790 *
791 * @return a non-modifiable list of supported mandatory stream combinations or
792 * null in case device is not backward compatible or the method encounters
793 * an error.
794 */
Emilian Peev0081bcf2019-02-26 14:55:58 -0800795 public @Nullable List<MandatoryStreamCombination>
796 getAvailableMandatoryStreamCombinations() {
Emilian Peev423cbd72018-11-10 18:37:45 +0000797 if (!isColorOutputSupported()) {
798 Log.v(TAG, "Device is not backward compatible!");
799 return null;
800 }
801
802 if ((mCameraId < 0) && !isExternalCamera()) {
803 Log.i(TAG, "Invalid camera id");
804 return null;
805 }
806
807 ArrayList<StreamCombinationTemplate> availableTemplates =
808 new ArrayList<StreamCombinationTemplate> ();
809 if (isHardwareLevelAtLeastLegacy()) {
810 availableTemplates.addAll(Arrays.asList(sLegacyCombinations));
811 }
812
813 // External devices are identical to limited devices w.r.t. stream combinations.
814 if (isHardwareLevelAtLeastLimited() || isExternalCamera()) {
815 availableTemplates.addAll(Arrays.asList(sLimitedCombinations));
Emilian Peev275da0b2018-11-15 18:20:24 +0000816
817 if (isPrivateReprocessingSupported()) {
818 availableTemplates.addAll(Arrays.asList(sLimitedPrivateReprocCombinations));
819 }
820
821 if (isYUVReprocessingSupported()) {
822 availableTemplates.addAll(Arrays.asList(sLimitedYUVReprocCombinations));
823 }
824
Emilian Peev423cbd72018-11-10 18:37:45 +0000825 }
826
827 if (isCapabilitySupported(
828 CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE)) {
829 availableTemplates.addAll(Arrays.asList(sBurstCombinations));
830 }
831
832 if (isHardwareLevelAtLeastFull()) {
833 availableTemplates.addAll(Arrays.asList(sFullCombinations));
Emilian Peev275da0b2018-11-15 18:20:24 +0000834
835 if (isPrivateReprocessingSupported()) {
836 availableTemplates.addAll(Arrays.asList(sFullPrivateReprocCombinations));
837 }
838
839 if (isYUVReprocessingSupported()) {
840 availableTemplates.addAll(Arrays.asList(sFullYUVReprocCombinations));
841 }
842
Emilian Peev423cbd72018-11-10 18:37:45 +0000843 }
844
845 if (isCapabilitySupported(
846 CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW)) {
847 availableTemplates.addAll(Arrays.asList(sRawCombinations));
Emilian Peev275da0b2018-11-15 18:20:24 +0000848
849 if (isPrivateReprocessingSupported()) {
850 availableTemplates.addAll(Arrays.asList(sRAWPrivateReprocCombinations));
851 }
852
853 if (isYUVReprocessingSupported()) {
854 availableTemplates.addAll(Arrays.asList(sRAWYUVReprocCombinations));
855 }
856
Emilian Peev423cbd72018-11-10 18:37:45 +0000857 }
858
859 if (isHardwareLevelAtLeastLevel3()) {
860 availableTemplates.addAll(Arrays.asList(sLevel3Combinations));
Emilian Peev275da0b2018-11-15 18:20:24 +0000861
862 if (isPrivateReprocessingSupported()) {
863 availableTemplates.addAll(Arrays.asList(sLevel3PrivateReprocCombinations));
864 }
865
866 if (isYUVReprocessingSupported()) {
867 availableTemplates.addAll(Arrays.asList(sLevel3YUVReprocCombinations));
868 }
869
Emilian Peev423cbd72018-11-10 18:37:45 +0000870 }
871
872 return generateAvailableCombinations(availableTemplates);
873 }
874
875 /**
876 * Helper method to generate the available stream combinations given the
877 * list of available combination templates.
878 *
879 * @param availableTemplates a list of templates supported by the camera device.
880 * @return a non-modifiable list of supported mandatory stream combinations or
881 * null in case of errors.
882 */
Emilian Peev0081bcf2019-02-26 14:55:58 -0800883 private @Nullable List<MandatoryStreamCombination> generateAvailableCombinations(
884 @NonNull ArrayList<StreamCombinationTemplate> availableTemplates) {
Emilian Peev423cbd72018-11-10 18:37:45 +0000885 if (availableTemplates.isEmpty()) {
886 Log.e(TAG, "No available stream templates!");
887 return null;
888 }
889
890 HashMap<Pair<SizeThreshold, Integer>, List<Size>> availableSizes =
891 enumerateAvailableSizes();
892 if (availableSizes == null) {
893 Log.e(TAG, "Available size enumeration failed!");
894 return null;
895 }
896
897 // RAW only uses MAXIMUM size threshold
898 Size[] rawSizes = mStreamConfigMap.getOutputSizes(ImageFormat.RAW_SENSOR);
899 ArrayList<Size> availableRawSizes = new ArrayList<Size>();
900 if (rawSizes != null) {
901 availableRawSizes.ensureCapacity(rawSizes.length);
902 availableRawSizes.addAll(Arrays.asList(rawSizes));
903 }
904
Emilian Peev275da0b2018-11-15 18:20:24 +0000905 Size maxPrivateInputSize = new Size(0, 0);
906 if (isPrivateReprocessingSupported()) {
907 maxPrivateInputSize = getMaxSize(mStreamConfigMap.getInputSizes(
908 ImageFormat.PRIVATE));
909 }
910
911 Size maxYUVInputSize = new Size(0, 0);
912 if (isYUVReprocessingSupported()) {
913 maxYUVInputSize = getMaxSize(mStreamConfigMap.getInputSizes(
914 ImageFormat.YUV_420_888));
915 }
916
Emilian Peev423cbd72018-11-10 18:37:45 +0000917 // Generate the available mandatory stream combinations given the supported templates
918 // and size ranges.
919 ArrayList<MandatoryStreamCombination> availableStreamCombinations =
920 new ArrayList<MandatoryStreamCombination>();
921 availableStreamCombinations.ensureCapacity(availableTemplates.size());
922 for (StreamCombinationTemplate combTemplate : availableTemplates) {
923 ArrayList<MandatoryStreamInformation> streamsInfo =
924 new ArrayList<MandatoryStreamInformation>();
925 streamsInfo.ensureCapacity(combTemplate.mStreamTemplates.length);
Emilian Peev275da0b2018-11-15 18:20:24 +0000926 boolean isReprocessable = combTemplate.mReprocessType != ReprocessType.NONE;
927 if (isReprocessable) {
928 // The first and second streams in a reprocessable combination have the
929 // same size and format. The first is the input and the second is the output
930 // used for generating the subsequent input buffers.
931 ArrayList<Size> inputSize = new ArrayList<Size>();
932 int format;
933 if (combTemplate.mReprocessType == ReprocessType.PRIVATE) {
934 inputSize.add(maxPrivateInputSize);
935 format = ImageFormat.PRIVATE;
936 } else {
937 inputSize.add(maxYUVInputSize);
938 format = ImageFormat.YUV_420_888;
939 }
940
941 streamsInfo.add(new MandatoryStreamInformation(inputSize, format,
942 /*isInput*/true));
943 streamsInfo.add(new MandatoryStreamInformation(inputSize, format));
944 }
945
Emilian Peev423cbd72018-11-10 18:37:45 +0000946 for (StreamTemplate template : combTemplate.mStreamTemplates) {
947 List<Size> sizes = null;
948 if (template.mFormat == ImageFormat.RAW_SENSOR) {
949 sizes = availableRawSizes;
950 } else {
951 Pair<SizeThreshold, Integer> pair;
952 pair = new Pair<SizeThreshold, Integer>(template.mSizeThreshold,
953 new Integer(template.mFormat));
954 sizes = availableSizes.get(pair);
955 }
956
957 MandatoryStreamInformation streamInfo;
958 try {
959 streamInfo = new MandatoryStreamInformation(sizes, template.mFormat);
960 } catch (IllegalArgumentException e) {
961 Log.e(TAG, "No available sizes found for format: " + template.mFormat +
962 " size threshold: " + template.mSizeThreshold + " combination: " +
963 combTemplate.mDescription);
964 return null;
965 }
966
967 streamsInfo.add(streamInfo);
968 }
969
970 MandatoryStreamCombination streamCombination;
971 try {
972 streamCombination = new MandatoryStreamCombination(streamsInfo,
Emilian Peev275da0b2018-11-15 18:20:24 +0000973 combTemplate.mDescription, isReprocessable);
Emilian Peev423cbd72018-11-10 18:37:45 +0000974 } catch (IllegalArgumentException e) {
975 Log.e(TAG, "No stream information for mandatory combination: "
976 + combTemplate.mDescription);
977 return null;
978 }
979
980 availableStreamCombinations.add(streamCombination);
981 }
982
983 return Collections.unmodifiableList(availableStreamCombinations);
984 }
985
986 /**
987 * Helper method to enumerate all available sizes according to size threshold and format.
988 */
Emilian Peev0081bcf2019-02-26 14:55:58 -0800989 private @Nullable HashMap<Pair<SizeThreshold, Integer>, List<Size>>
990 enumerateAvailableSizes() {
Emilian Peev423cbd72018-11-10 18:37:45 +0000991 final int[] formats = {
992 ImageFormat.PRIVATE,
993 ImageFormat.YUV_420_888,
994 ImageFormat.JPEG
995 };
996 Size recordingMaxSize = new Size(0, 0);
997 Size previewMaxSize = new Size(0, 0);
998 Size vgaSize = new Size(640, 480);
Shuzhen Wangaae38962019-05-30 10:11:47 -0700999 // For external camera, or hidden physical camera, CamcorderProfile may not be
1000 // available, so get maximum recording size using stream configuration map.
1001 if (isExternalCamera() || mIsHiddenPhysicalCamera) {
1002 recordingMaxSize = getMaxCameraRecordingSize();
Emilian Peev423cbd72018-11-10 18:37:45 +00001003 } else {
1004 recordingMaxSize = getMaxRecordingSize();
1005 }
1006 if (recordingMaxSize == null) {
1007 Log.e(TAG, "Failed to find maximum recording size!");
1008 return null;
1009 }
1010
1011 HashMap<Integer, Size[]> allSizes = new HashMap<Integer, Size[]>();
1012 for (int format : formats) {
1013 Integer intFormat = new Integer(format);
1014 allSizes.put(intFormat, mStreamConfigMap.getOutputSizes(format));
1015 }
1016
1017 List<Size> previewSizes = getSizesWithinBound(
1018 allSizes.get(new Integer(ImageFormat.PRIVATE)), kPreviewSizeBound);
1019 if ((previewSizes == null) || (previewSizes.isEmpty())) {
1020 Log.e(TAG, "No preview sizes within preview size bound!");
1021 return null;
1022 }
1023 List<Size> orderedPreviewSizes = getAscendingOrderSizes(previewSizes,
1024 /*ascending*/false);
1025 previewMaxSize = getMaxPreviewSize(orderedPreviewSizes);
1026
1027 HashMap<Pair<SizeThreshold, Integer>, List<Size>> availableSizes =
1028 new HashMap<Pair<SizeThreshold, Integer>, List<Size>>();
1029
1030 for (int format : formats) {
1031 Integer intFormat = new Integer(format);
1032 Size[] sizes = allSizes.get(intFormat);
1033 Pair<SizeThreshold, Integer> pair = new Pair<SizeThreshold, Integer>(
1034 SizeThreshold.VGA, intFormat);
1035 availableSizes.put(pair, getSizesWithinBound(sizes, vgaSize));
1036
1037 pair = new Pair<SizeThreshold, Integer>(SizeThreshold.PREVIEW, intFormat);
1038 availableSizes.put(pair, getSizesWithinBound(sizes, previewMaxSize));
1039
1040 pair = new Pair<SizeThreshold, Integer>(SizeThreshold.RECORD, intFormat);
1041 availableSizes.put(pair, getSizesWithinBound(sizes, recordingMaxSize));
1042
1043 pair = new Pair<SizeThreshold, Integer>(SizeThreshold.MAXIMUM, intFormat);
1044 availableSizes.put(pair, Arrays.asList(sizes));
1045 }
1046
1047 return availableSizes;
1048 }
1049
1050 /**
1051 * Compile a list of sizes smaller than or equal to given bound.
1052 * Return an empty list if there is no size smaller than or equal to the bound.
1053 */
Emilian Peev0081bcf2019-02-26 14:55:58 -08001054 private static @Nullable List<Size> getSizesWithinBound(@NonNull Size[] sizes,
1055 @NonNull Size bound) {
Emilian Peev423cbd72018-11-10 18:37:45 +00001056 ArrayList<Size> ret = new ArrayList<Size>();
1057 for (Size size : sizes) {
1058 if (size.getWidth() <= bound.getWidth() && size.getHeight() <= bound.getHeight()) {
1059 ret.add(size);
1060 }
1061 }
1062
1063 return ret;
1064 }
1065
1066 /**
Jayant Chowdharya24f94b2020-01-14 11:37:46 -08001067 * Return the lower size
1068 */
1069 public static @Nullable Size getMinSize(Size a, Size b) {
1070 if (a == null || b == null) {
1071 throw new IllegalArgumentException("sizes was empty");
1072 }
1073 if (a.getWidth() * a.getHeight() < b.getHeight() * b.getWidth()) {
1074 return a;
1075 }
1076 return b;
1077 }
1078 /**
Emilian Peev275da0b2018-11-15 18:20:24 +00001079 * Get the largest size by area.
1080 *
1081 * @param sizes an array of sizes, must have at least 1 element
1082 *
1083 * @return Largest Size
1084 *
1085 * @throws IllegalArgumentException if sizes was null or had 0 elements
1086 */
Emilian Peev0081bcf2019-02-26 14:55:58 -08001087 public static @Nullable Size getMaxSize(@NonNull Size... sizes) {
Emilian Peev275da0b2018-11-15 18:20:24 +00001088 if (sizes == null || sizes.length == 0) {
1089 throw new IllegalArgumentException("sizes was empty");
1090 }
1091
1092 Size sz = sizes[0];
1093 for (Size size : sizes) {
1094 if (size.getWidth() * size.getHeight() > sz.getWidth() * sz.getHeight()) {
1095 sz = size;
1096 }
1097 }
1098
1099 return sz;
1100 }
1101
1102 /**
Emilian Peev423cbd72018-11-10 18:37:45 +00001103 * Whether or not the hardware level reported by android.info.supportedHardwareLevel is
1104 * at least the desired one (but could be higher)
1105 */
1106 private boolean isHardwareLevelAtLeast(int level) {
1107 final int[] sortedHwLevels = {
1108 CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY,
1109 CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_EXTERNAL,
1110 CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
1111 CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL,
1112 CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_3
1113 };
1114 if (level == mHwLevel) {
1115 return true;
1116 }
1117
1118 for (int sortedlevel : sortedHwLevels) {
1119 if (sortedlevel == level) {
1120 return true;
1121 } else if (sortedlevel == mHwLevel) {
1122 return false;
1123 }
1124 }
1125
1126 return false;
1127 }
1128
1129 /**
1130 * Whether or not the camera is an external camera.
1131 *
1132 * @return {@code true} if the device is external, {@code false} otherwise.
1133 */
1134 private boolean isExternalCamera() {
1135 return mHwLevel == CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_EXTERNAL;
1136 }
1137
1138 /**
1139 * Whether or not the hardware level is at least legacy.
1140 *
1141 * @return {@code true} if the device is {@code LEGACY}, {@code false} otherwise.
1142 */
1143 private boolean isHardwareLevelAtLeastLegacy() {
1144 return isHardwareLevelAtLeast(CameraMetadata.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY);
1145 }
1146
1147 /**
1148 * Whether or not the hardware level is at least limited.
1149 *
1150 * @return {@code true} if the device is {@code LIMITED} or {@code FULL},
1151 * {@code false} otherwise (i.e. LEGACY).
1152 */
1153 private boolean isHardwareLevelAtLeastLimited() {
1154 return isHardwareLevelAtLeast(CameraMetadata.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED);
1155 }
1156
1157 /**
1158 * Whether or not the hardware level is at least full.
1159 *
1160 * @return {@code true} if the device is {@code FULL}, {@code false} otherwise.
1161 */
1162 private boolean isHardwareLevelAtLeastFull() {
1163 return isHardwareLevelAtLeast(CameraMetadata.INFO_SUPPORTED_HARDWARE_LEVEL_FULL);
1164 }
1165
1166 /**
1167 * Whether or not the hardware level is at least Level 3.
1168 *
1169 * @return {@code true} if the device is {@code LEVEL3}, {@code false} otherwise.
1170 */
1171 private boolean isHardwareLevelAtLeastLevel3() {
1172 return isHardwareLevelAtLeast(CameraMetadata.INFO_SUPPORTED_HARDWARE_LEVEL_3);
1173 }
1174
1175 /**
1176 * Determine whether the current device supports a capability or not.
1177 *
1178 * @param capability (non-negative)
1179 *
1180 * @return {@code true} if the capability is supported, {@code false} otherwise.
1181 *
1182 */
1183 private boolean isCapabilitySupported(int capability) {
1184 return mCapabilities.contains(capability);
1185 }
1186
1187 /**
1188 * Check whether the current device is backward compatible.
1189 */
1190 private boolean isColorOutputSupported() {
1191 return isCapabilitySupported(
1192 CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE);
1193 }
1194
1195 /**
Emilian Peev275da0b2018-11-15 18:20:24 +00001196 * Check whether the current device supports private reprocessing.
1197 */
1198 private boolean isPrivateReprocessingSupported() {
1199 return isCapabilitySupported(
1200 CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_PRIVATE_REPROCESSING);
1201 }
1202
1203 /**
1204 * Check whether the current device supports YUV reprocessing.
1205 */
1206 private boolean isYUVReprocessingSupported() {
1207 return isCapabilitySupported(
1208 CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_YUV_REPROCESSING);
1209 }
1210
1211 /**
Emilian Peev423cbd72018-11-10 18:37:45 +00001212 * Return the maximum supported video size using the camcorder profile information.
1213 *
1214 * @return Maximum supported video size.
1215 */
Emilian Peev0081bcf2019-02-26 14:55:58 -08001216 private @Nullable Size getMaxRecordingSize() {
Emilian Peev423cbd72018-11-10 18:37:45 +00001217 int quality =
1218 CamcorderProfile.hasProfile(mCameraId, CamcorderProfile.QUALITY_2160P) ?
1219 CamcorderProfile.QUALITY_2160P :
1220 CamcorderProfile.hasProfile(mCameraId, CamcorderProfile.QUALITY_1080P) ?
1221 CamcorderProfile.QUALITY_1080P :
1222 CamcorderProfile.hasProfile(mCameraId, CamcorderProfile.QUALITY_720P) ?
1223 CamcorderProfile.QUALITY_720P :
1224 CamcorderProfile.hasProfile(mCameraId, CamcorderProfile.QUALITY_480P) ?
1225 CamcorderProfile.QUALITY_480P :
1226 CamcorderProfile.hasProfile(mCameraId, CamcorderProfile.QUALITY_QVGA) ?
1227 CamcorderProfile.QUALITY_QVGA :
1228 CamcorderProfile.hasProfile(mCameraId, CamcorderProfile.QUALITY_CIF) ?
1229 CamcorderProfile.QUALITY_CIF :
1230 CamcorderProfile.hasProfile(mCameraId, CamcorderProfile.QUALITY_QCIF) ?
1231 CamcorderProfile.QUALITY_QCIF :
1232 -1;
1233
1234 if (quality < 0) {
1235 return null;
1236 }
1237
1238 CamcorderProfile maxProfile = CamcorderProfile.get(mCameraId, quality);
1239 return new Size(maxProfile.videoFrameWidth, maxProfile.videoFrameHeight);
1240 }
1241
1242 /**
Shuzhen Wangaae38962019-05-30 10:11:47 -07001243 * Return the maximum supported video size for cameras using data from
Emilian Peev423cbd72018-11-10 18:37:45 +00001244 * the stream configuration map.
1245 *
1246 * @return Maximum supported video size.
1247 */
Shuzhen Wangaae38962019-05-30 10:11:47 -07001248 private @NonNull Size getMaxCameraRecordingSize() {
Emilian Peev423cbd72018-11-10 18:37:45 +00001249 final Size FULLHD = new Size(1920, 1080);
1250
1251 Size[] videoSizeArr = mStreamConfigMap.getOutputSizes(
1252 android.media.MediaRecorder.class);
1253 List<Size> sizes = new ArrayList<Size>();
1254 for (Size sz: videoSizeArr) {
1255 if (sz.getWidth() <= FULLHD.getWidth() && sz.getHeight() <= FULLHD.getHeight()) {
1256 sizes.add(sz);
1257 }
1258 }
1259 List<Size> videoSizes = getAscendingOrderSizes(sizes, /*ascending*/false);
1260 for (Size sz : videoSizes) {
1261 long minFrameDuration = mStreamConfigMap.getOutputMinFrameDuration(
1262 android.media.MediaRecorder.class, sz);
1263 // Give some margin for rounding error
1264 if (minFrameDuration > (1e9 / 30.1)) {
1265 Log.i(TAG, "External camera " + mCameraId + " has max video size:" + sz);
1266 return sz;
1267 }
1268 }
1269 Log.w(TAG, "Camera " + mCameraId + " does not support any 30fps video output");
1270 return FULLHD; // doesn't matter what size is returned here
1271 }
1272
Emilian Peev0081bcf2019-02-26 14:55:58 -08001273 private @NonNull Size getMaxPreviewSize(List<Size> orderedPreviewSizes) {
Emilian Peev423cbd72018-11-10 18:37:45 +00001274 if (orderedPreviewSizes != null) {
1275 for (Size size : orderedPreviewSizes) {
1276 if ((mDisplaySize.getWidth() >= size.getWidth()) &&
Emilian Peev8649a912019-12-16 13:57:12 -08001277 (mDisplaySize.getHeight() >= size.getHeight())) {
Emilian Peev423cbd72018-11-10 18:37:45 +00001278 return size;
1279 }
1280 }
1281 }
1282
1283 Log.w(TAG,"Camera " + mCameraId + " maximum preview size search failed with "
1284 + "display size " + mDisplaySize);
1285 return kPreviewSizeBound;
1286 }
1287
1288 /**
1289 * Size comparison method used by size comparators.
1290 */
1291 private static int compareSizes(int widthA, int heightA, int widthB, int heightB) {
1292 long left = widthA * (long) heightA;
1293 long right = widthB * (long) heightB;
1294 if (left == right) {
1295 left = widthA;
1296 right = widthB;
1297 }
1298 return (left < right) ? -1 : (left > right ? 1 : 0);
1299 }
1300
1301 /**
1302 * Size comparator that compares the number of pixels it covers.
1303 *
1304 * <p>If two the areas of two sizes are same, compare the widths.</p>
1305 */
1306 public static class SizeComparator implements Comparator<Size> {
1307 @Override
Emilian Peev0081bcf2019-02-26 14:55:58 -08001308 public int compare(@NonNull Size lhs, @NonNull Size rhs) {
Emilian Peev423cbd72018-11-10 18:37:45 +00001309 return compareSizes(lhs.getWidth(), lhs.getHeight(), rhs.getWidth(),
1310 rhs.getHeight());
1311 }
1312 }
1313
1314 /**
1315 * Get a sorted list of sizes from a given size list.
1316 *
1317 * <p>
1318 * The size is compare by area it covers, if the areas are same, then
1319 * compare the widths.
1320 * </p>
1321 *
1322 * @param sizeList The input size list to be sorted
1323 * @param ascending True if the order is ascending, otherwise descending order
1324 * @return The ordered list of sizes
1325 */
Emilian Peev0081bcf2019-02-26 14:55:58 -08001326 private static @NonNull List<Size> getAscendingOrderSizes(
1327 @NonNull final List<Size> sizeList, boolean ascending) {
Emilian Peev423cbd72018-11-10 18:37:45 +00001328 Comparator<Size> comparator = new SizeComparator();
1329 List<Size> sortedSizes = new ArrayList<Size>();
1330 sortedSizes.addAll(sizeList);
1331 Collections.sort(sortedSizes, comparator);
1332 if (!ascending) {
1333 Collections.reverse(sortedSizes);
1334 }
1335
1336 return sortedSizes;
1337 }
1338 }
1339}