blob: 776d155e5b3e55e6de0fe826b6861f81948ea51f [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
Jayant Chowdharyd7feae02020-06-22 23:13:54 -0700688 private static StreamCombinationTemplate sConcurrentDepthOnlyStreamCombinations[] = {
689 new StreamCombinationTemplate(new StreamTemplate [] {
690 new StreamTemplate(ImageFormat.DEPTH16, SizeThreshold.VGA) },
691 "Depth capture for mesh based object rendering"),
692 };
693
Emilian Peev423cbd72018-11-10 18:37:45 +0000694 /**
695 * Helper builder class to generate a list of available mandatory stream combinations.
696 * @hide
697 */
698 public static final class Builder {
699 private Size mDisplaySize;
700 private List<Integer> mCapabilities;
701 private int mHwLevel, mCameraId;
702 private StreamConfigurationMap mStreamConfigMap;
Shuzhen Wangaae38962019-05-30 10:11:47 -0700703 private boolean mIsHiddenPhysicalCamera;
Emilian Peev423cbd72018-11-10 18:37:45 +0000704
705 private final Size kPreviewSizeBound = new Size(1920, 1088);
706
707 /**
708 * Helper class to be used to generate the available mandatory stream combinations.
709 *
710 * @param cameraId Current camera id.
711 * @param hwLevel The camera HW level as reported by android.info.supportedHardwareLevel.
712 * @param displaySize The device display size.
713 * @param capabilities The camera device capabilities.
714 * @param sm The camera device stream configuration map.
715 */
716 public Builder(int cameraId, int hwLevel, @NonNull Size displaySize,
717 @NonNull List<Integer> capabilities, @NonNull StreamConfigurationMap sm) {
718 mCameraId = cameraId;
719 mDisplaySize = displaySize;
720 mCapabilities = capabilities;
721 mStreamConfigMap = sm;
722 mHwLevel = hwLevel;
Shuzhen Wangaae38962019-05-30 10:11:47 -0700723 mIsHiddenPhysicalCamera =
724 CameraManager.isHiddenPhysicalCamera(Integer.toString(mCameraId));
Emilian Peev423cbd72018-11-10 18:37:45 +0000725 }
726
727 /**
Jayant Chowdharya24f94b2020-01-14 11:37:46 -0800728 * Retrieve a list of all available mandatory concurrent stream combinations.
729 * This method should only be called for devices which are listed in combinations returned
Jayant Chowdhary44bc7d82020-03-10 11:09:57 -0700730 * by CameraManager.getConcurrentCameraIds.
Jayant Chowdharya24f94b2020-01-14 11:37:46 -0800731 *
732 * @return a non-modifiable list of supported mandatory concurrent stream combinations.
733 */
734 public @NonNull List<MandatoryStreamCombination>
735 getAvailableMandatoryConcurrentStreamCombinations() {
736 // Since concurrent streaming support is optional, we mandate these stream
737 // combinations regardless of camera device capabilities.
Jayant Chowdharyd7feae02020-06-22 23:13:54 -0700738
739 StreamCombinationTemplate []chosenStreamCombinations = sConcurrentStreamCombinations;
Jayant Chowdharya24f94b2020-01-14 11:37:46 -0800740 if (!isColorOutputSupported()) {
Jayant Chowdharyd7feae02020-06-22 23:13:54 -0700741 Log.v(TAG, "Device is not backward compatible, depth streams are mandatory!");
742 chosenStreamCombinations = sConcurrentDepthOnlyStreamCombinations;
Jayant Chowdharya24f94b2020-01-14 11:37:46 -0800743 }
Jayant Chowdharyd7feae02020-06-22 23:13:54 -0700744 Size sizeVGAp = new Size(640, 480);
Jayant Chowdharya24f94b2020-01-14 11:37:46 -0800745 Size size720p = new Size(1280, 720);
Jayant Chowdharyac404c12020-02-11 20:37:22 -0800746 Size size1440p = new Size(1920, 1440);
Jayant Chowdharya24f94b2020-01-14 11:37:46 -0800747
748 ArrayList<MandatoryStreamCombination> availableConcurrentStreamCombinations =
749 new ArrayList<MandatoryStreamCombination>();
750 availableConcurrentStreamCombinations.ensureCapacity(
Jayant Chowdharyd7feae02020-06-22 23:13:54 -0700751 chosenStreamCombinations.length);
752 for (StreamCombinationTemplate combTemplate : chosenStreamCombinations) {
Jayant Chowdharya24f94b2020-01-14 11:37:46 -0800753 ArrayList<MandatoryStreamInformation> streamsInfo =
754 new ArrayList<MandatoryStreamInformation>();
755 streamsInfo.ensureCapacity(combTemplate.mStreamTemplates.length);
756 for (StreamTemplate template : combTemplate.mStreamTemplates) {
757 MandatoryStreamInformation streamInfo;
758 List<Size> sizes = new ArrayList<Size>();
Jayant Chowdharyac404c12020-02-11 20:37:22 -0800759 Size formatSize = null;
760 switch (template.mSizeThreshold) {
761 case s1440p:
762 formatSize = size1440p;
763 break;
Jayant Chowdharyd7feae02020-06-22 23:13:54 -0700764 case VGA:
765 formatSize = sizeVGAp;
766 break;
Jayant Chowdharyac404c12020-02-11 20:37:22 -0800767 default:
768 formatSize = size720p;
769 }
Jayant Chowdharya24f94b2020-01-14 11:37:46 -0800770 Size sizeChosen =
Jayant Chowdharyac404c12020-02-11 20:37:22 -0800771 getMinSize(formatSize,
Jayant Chowdharya24f94b2020-01-14 11:37:46 -0800772 getMaxSize(mStreamConfigMap.getOutputSizes(template.mFormat)));
773 sizes.add(sizeChosen);
774 try {
775 streamInfo = new MandatoryStreamInformation(sizes, template.mFormat);
776 } catch (IllegalArgumentException e) {
777 String cause = "No available sizes found for format: " + template.mFormat
778 + " size threshold: " + template.mSizeThreshold + " combination: "
779 + combTemplate.mDescription;
780 throw new RuntimeException(cause, e);
781 }
782 streamsInfo.add(streamInfo);
783 }
784
785 MandatoryStreamCombination streamCombination;
786 try {
787 streamCombination = new MandatoryStreamCombination(streamsInfo,
788 combTemplate.mDescription, /*isReprocess*/false);
789 } catch (IllegalArgumentException e) {
790 String cause = "No stream information for mandatory combination: "
791 + combTemplate.mDescription;
792 throw new RuntimeException(cause, e);
793 }
794 availableConcurrentStreamCombinations.add(streamCombination);
795 }
796 return Collections.unmodifiableList(availableConcurrentStreamCombinations);
797 }
798
799 /**
Emilian Peev423cbd72018-11-10 18:37:45 +0000800 * Retrieve a list of all available mandatory stream combinations.
801 *
802 * @return a non-modifiable list of supported mandatory stream combinations or
803 * null in case device is not backward compatible or the method encounters
804 * an error.
805 */
Emilian Peev0081bcf2019-02-26 14:55:58 -0800806 public @Nullable List<MandatoryStreamCombination>
807 getAvailableMandatoryStreamCombinations() {
Emilian Peev423cbd72018-11-10 18:37:45 +0000808 if (!isColorOutputSupported()) {
809 Log.v(TAG, "Device is not backward compatible!");
810 return null;
811 }
812
813 if ((mCameraId < 0) && !isExternalCamera()) {
814 Log.i(TAG, "Invalid camera id");
815 return null;
816 }
817
818 ArrayList<StreamCombinationTemplate> availableTemplates =
819 new ArrayList<StreamCombinationTemplate> ();
820 if (isHardwareLevelAtLeastLegacy()) {
821 availableTemplates.addAll(Arrays.asList(sLegacyCombinations));
822 }
823
824 // External devices are identical to limited devices w.r.t. stream combinations.
825 if (isHardwareLevelAtLeastLimited() || isExternalCamera()) {
826 availableTemplates.addAll(Arrays.asList(sLimitedCombinations));
Emilian Peev275da0b2018-11-15 18:20:24 +0000827
828 if (isPrivateReprocessingSupported()) {
829 availableTemplates.addAll(Arrays.asList(sLimitedPrivateReprocCombinations));
830 }
831
832 if (isYUVReprocessingSupported()) {
833 availableTemplates.addAll(Arrays.asList(sLimitedYUVReprocCombinations));
834 }
835
Emilian Peev423cbd72018-11-10 18:37:45 +0000836 }
837
838 if (isCapabilitySupported(
839 CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE)) {
840 availableTemplates.addAll(Arrays.asList(sBurstCombinations));
841 }
842
843 if (isHardwareLevelAtLeastFull()) {
844 availableTemplates.addAll(Arrays.asList(sFullCombinations));
Emilian Peev275da0b2018-11-15 18:20:24 +0000845
846 if (isPrivateReprocessingSupported()) {
847 availableTemplates.addAll(Arrays.asList(sFullPrivateReprocCombinations));
848 }
849
850 if (isYUVReprocessingSupported()) {
851 availableTemplates.addAll(Arrays.asList(sFullYUVReprocCombinations));
852 }
853
Emilian Peev423cbd72018-11-10 18:37:45 +0000854 }
855
856 if (isCapabilitySupported(
857 CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW)) {
858 availableTemplates.addAll(Arrays.asList(sRawCombinations));
Emilian Peev275da0b2018-11-15 18:20:24 +0000859
860 if (isPrivateReprocessingSupported()) {
861 availableTemplates.addAll(Arrays.asList(sRAWPrivateReprocCombinations));
862 }
863
864 if (isYUVReprocessingSupported()) {
865 availableTemplates.addAll(Arrays.asList(sRAWYUVReprocCombinations));
866 }
867
Emilian Peev423cbd72018-11-10 18:37:45 +0000868 }
869
870 if (isHardwareLevelAtLeastLevel3()) {
871 availableTemplates.addAll(Arrays.asList(sLevel3Combinations));
Emilian Peev275da0b2018-11-15 18:20:24 +0000872
873 if (isPrivateReprocessingSupported()) {
874 availableTemplates.addAll(Arrays.asList(sLevel3PrivateReprocCombinations));
875 }
876
877 if (isYUVReprocessingSupported()) {
878 availableTemplates.addAll(Arrays.asList(sLevel3YUVReprocCombinations));
879 }
880
Emilian Peev423cbd72018-11-10 18:37:45 +0000881 }
882
883 return generateAvailableCombinations(availableTemplates);
884 }
885
886 /**
887 * Helper method to generate the available stream combinations given the
888 * list of available combination templates.
889 *
890 * @param availableTemplates a list of templates supported by the camera device.
891 * @return a non-modifiable list of supported mandatory stream combinations or
892 * null in case of errors.
893 */
Emilian Peev0081bcf2019-02-26 14:55:58 -0800894 private @Nullable List<MandatoryStreamCombination> generateAvailableCombinations(
895 @NonNull ArrayList<StreamCombinationTemplate> availableTemplates) {
Emilian Peev423cbd72018-11-10 18:37:45 +0000896 if (availableTemplates.isEmpty()) {
897 Log.e(TAG, "No available stream templates!");
898 return null;
899 }
900
901 HashMap<Pair<SizeThreshold, Integer>, List<Size>> availableSizes =
902 enumerateAvailableSizes();
903 if (availableSizes == null) {
904 Log.e(TAG, "Available size enumeration failed!");
905 return null;
906 }
907
908 // RAW only uses MAXIMUM size threshold
909 Size[] rawSizes = mStreamConfigMap.getOutputSizes(ImageFormat.RAW_SENSOR);
910 ArrayList<Size> availableRawSizes = new ArrayList<Size>();
911 if (rawSizes != null) {
912 availableRawSizes.ensureCapacity(rawSizes.length);
913 availableRawSizes.addAll(Arrays.asList(rawSizes));
914 }
915
Emilian Peev275da0b2018-11-15 18:20:24 +0000916 Size maxPrivateInputSize = new Size(0, 0);
917 if (isPrivateReprocessingSupported()) {
918 maxPrivateInputSize = getMaxSize(mStreamConfigMap.getInputSizes(
919 ImageFormat.PRIVATE));
920 }
921
922 Size maxYUVInputSize = new Size(0, 0);
923 if (isYUVReprocessingSupported()) {
924 maxYUVInputSize = getMaxSize(mStreamConfigMap.getInputSizes(
925 ImageFormat.YUV_420_888));
926 }
927
Emilian Peev423cbd72018-11-10 18:37:45 +0000928 // Generate the available mandatory stream combinations given the supported templates
929 // and size ranges.
930 ArrayList<MandatoryStreamCombination> availableStreamCombinations =
931 new ArrayList<MandatoryStreamCombination>();
932 availableStreamCombinations.ensureCapacity(availableTemplates.size());
933 for (StreamCombinationTemplate combTemplate : availableTemplates) {
934 ArrayList<MandatoryStreamInformation> streamsInfo =
935 new ArrayList<MandatoryStreamInformation>();
936 streamsInfo.ensureCapacity(combTemplate.mStreamTemplates.length);
Emilian Peev275da0b2018-11-15 18:20:24 +0000937 boolean isReprocessable = combTemplate.mReprocessType != ReprocessType.NONE;
938 if (isReprocessable) {
939 // The first and second streams in a reprocessable combination have the
940 // same size and format. The first is the input and the second is the output
941 // used for generating the subsequent input buffers.
942 ArrayList<Size> inputSize = new ArrayList<Size>();
943 int format;
944 if (combTemplate.mReprocessType == ReprocessType.PRIVATE) {
945 inputSize.add(maxPrivateInputSize);
946 format = ImageFormat.PRIVATE;
947 } else {
948 inputSize.add(maxYUVInputSize);
949 format = ImageFormat.YUV_420_888;
950 }
951
952 streamsInfo.add(new MandatoryStreamInformation(inputSize, format,
953 /*isInput*/true));
954 streamsInfo.add(new MandatoryStreamInformation(inputSize, format));
955 }
956
Emilian Peev423cbd72018-11-10 18:37:45 +0000957 for (StreamTemplate template : combTemplate.mStreamTemplates) {
958 List<Size> sizes = null;
959 if (template.mFormat == ImageFormat.RAW_SENSOR) {
960 sizes = availableRawSizes;
961 } else {
962 Pair<SizeThreshold, Integer> pair;
963 pair = new Pair<SizeThreshold, Integer>(template.mSizeThreshold,
964 new Integer(template.mFormat));
965 sizes = availableSizes.get(pair);
966 }
967
968 MandatoryStreamInformation streamInfo;
969 try {
970 streamInfo = new MandatoryStreamInformation(sizes, template.mFormat);
971 } catch (IllegalArgumentException e) {
972 Log.e(TAG, "No available sizes found for format: " + template.mFormat +
973 " size threshold: " + template.mSizeThreshold + " combination: " +
974 combTemplate.mDescription);
975 return null;
976 }
977
978 streamsInfo.add(streamInfo);
979 }
980
981 MandatoryStreamCombination streamCombination;
982 try {
983 streamCombination = new MandatoryStreamCombination(streamsInfo,
Emilian Peev275da0b2018-11-15 18:20:24 +0000984 combTemplate.mDescription, isReprocessable);
Emilian Peev423cbd72018-11-10 18:37:45 +0000985 } catch (IllegalArgumentException e) {
986 Log.e(TAG, "No stream information for mandatory combination: "
987 + combTemplate.mDescription);
988 return null;
989 }
990
991 availableStreamCombinations.add(streamCombination);
992 }
993
994 return Collections.unmodifiableList(availableStreamCombinations);
995 }
996
997 /**
998 * Helper method to enumerate all available sizes according to size threshold and format.
999 */
Emilian Peev0081bcf2019-02-26 14:55:58 -08001000 private @Nullable HashMap<Pair<SizeThreshold, Integer>, List<Size>>
1001 enumerateAvailableSizes() {
Emilian Peev423cbd72018-11-10 18:37:45 +00001002 final int[] formats = {
1003 ImageFormat.PRIVATE,
1004 ImageFormat.YUV_420_888,
1005 ImageFormat.JPEG
1006 };
1007 Size recordingMaxSize = new Size(0, 0);
1008 Size previewMaxSize = new Size(0, 0);
1009 Size vgaSize = new Size(640, 480);
Shuzhen Wangaae38962019-05-30 10:11:47 -07001010 // For external camera, or hidden physical camera, CamcorderProfile may not be
1011 // available, so get maximum recording size using stream configuration map.
1012 if (isExternalCamera() || mIsHiddenPhysicalCamera) {
1013 recordingMaxSize = getMaxCameraRecordingSize();
Emilian Peev423cbd72018-11-10 18:37:45 +00001014 } else {
1015 recordingMaxSize = getMaxRecordingSize();
1016 }
1017 if (recordingMaxSize == null) {
1018 Log.e(TAG, "Failed to find maximum recording size!");
1019 return null;
1020 }
1021
1022 HashMap<Integer, Size[]> allSizes = new HashMap<Integer, Size[]>();
1023 for (int format : formats) {
1024 Integer intFormat = new Integer(format);
1025 allSizes.put(intFormat, mStreamConfigMap.getOutputSizes(format));
1026 }
1027
1028 List<Size> previewSizes = getSizesWithinBound(
1029 allSizes.get(new Integer(ImageFormat.PRIVATE)), kPreviewSizeBound);
1030 if ((previewSizes == null) || (previewSizes.isEmpty())) {
1031 Log.e(TAG, "No preview sizes within preview size bound!");
1032 return null;
1033 }
1034 List<Size> orderedPreviewSizes = getAscendingOrderSizes(previewSizes,
1035 /*ascending*/false);
1036 previewMaxSize = getMaxPreviewSize(orderedPreviewSizes);
1037
1038 HashMap<Pair<SizeThreshold, Integer>, List<Size>> availableSizes =
1039 new HashMap<Pair<SizeThreshold, Integer>, List<Size>>();
1040
1041 for (int format : formats) {
1042 Integer intFormat = new Integer(format);
1043 Size[] sizes = allSizes.get(intFormat);
1044 Pair<SizeThreshold, Integer> pair = new Pair<SizeThreshold, Integer>(
1045 SizeThreshold.VGA, intFormat);
1046 availableSizes.put(pair, getSizesWithinBound(sizes, vgaSize));
1047
1048 pair = new Pair<SizeThreshold, Integer>(SizeThreshold.PREVIEW, intFormat);
1049 availableSizes.put(pair, getSizesWithinBound(sizes, previewMaxSize));
1050
1051 pair = new Pair<SizeThreshold, Integer>(SizeThreshold.RECORD, intFormat);
1052 availableSizes.put(pair, getSizesWithinBound(sizes, recordingMaxSize));
1053
1054 pair = new Pair<SizeThreshold, Integer>(SizeThreshold.MAXIMUM, intFormat);
1055 availableSizes.put(pair, Arrays.asList(sizes));
1056 }
1057
1058 return availableSizes;
1059 }
1060
1061 /**
1062 * Compile a list of sizes smaller than or equal to given bound.
1063 * Return an empty list if there is no size smaller than or equal to the bound.
1064 */
Emilian Peev0081bcf2019-02-26 14:55:58 -08001065 private static @Nullable List<Size> getSizesWithinBound(@NonNull Size[] sizes,
1066 @NonNull Size bound) {
Emilian Peev423cbd72018-11-10 18:37:45 +00001067 ArrayList<Size> ret = new ArrayList<Size>();
1068 for (Size size : sizes) {
1069 if (size.getWidth() <= bound.getWidth() && size.getHeight() <= bound.getHeight()) {
1070 ret.add(size);
1071 }
1072 }
1073
1074 return ret;
1075 }
1076
1077 /**
Jayant Chowdharya24f94b2020-01-14 11:37:46 -08001078 * Return the lower size
1079 */
1080 public static @Nullable Size getMinSize(Size a, Size b) {
1081 if (a == null || b == null) {
1082 throw new IllegalArgumentException("sizes was empty");
1083 }
1084 if (a.getWidth() * a.getHeight() < b.getHeight() * b.getWidth()) {
1085 return a;
1086 }
1087 return b;
1088 }
1089 /**
Emilian Peev275da0b2018-11-15 18:20:24 +00001090 * Get the largest size by area.
1091 *
1092 * @param sizes an array of sizes, must have at least 1 element
1093 *
1094 * @return Largest Size
1095 *
1096 * @throws IllegalArgumentException if sizes was null or had 0 elements
1097 */
Emilian Peev0081bcf2019-02-26 14:55:58 -08001098 public static @Nullable Size getMaxSize(@NonNull Size... sizes) {
Emilian Peev275da0b2018-11-15 18:20:24 +00001099 if (sizes == null || sizes.length == 0) {
1100 throw new IllegalArgumentException("sizes was empty");
1101 }
1102
1103 Size sz = sizes[0];
1104 for (Size size : sizes) {
1105 if (size.getWidth() * size.getHeight() > sz.getWidth() * sz.getHeight()) {
1106 sz = size;
1107 }
1108 }
1109
1110 return sz;
1111 }
1112
1113 /**
Emilian Peev423cbd72018-11-10 18:37:45 +00001114 * Whether or not the hardware level reported by android.info.supportedHardwareLevel is
1115 * at least the desired one (but could be higher)
1116 */
1117 private boolean isHardwareLevelAtLeast(int level) {
1118 final int[] sortedHwLevels = {
1119 CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY,
1120 CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_EXTERNAL,
1121 CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
1122 CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL,
1123 CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_3
1124 };
1125 if (level == mHwLevel) {
1126 return true;
1127 }
1128
1129 for (int sortedlevel : sortedHwLevels) {
1130 if (sortedlevel == level) {
1131 return true;
1132 } else if (sortedlevel == mHwLevel) {
1133 return false;
1134 }
1135 }
1136
1137 return false;
1138 }
1139
1140 /**
1141 * Whether or not the camera is an external camera.
1142 *
1143 * @return {@code true} if the device is external, {@code false} otherwise.
1144 */
1145 private boolean isExternalCamera() {
1146 return mHwLevel == CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_EXTERNAL;
1147 }
1148
1149 /**
1150 * Whether or not the hardware level is at least legacy.
1151 *
1152 * @return {@code true} if the device is {@code LEGACY}, {@code false} otherwise.
1153 */
1154 private boolean isHardwareLevelAtLeastLegacy() {
1155 return isHardwareLevelAtLeast(CameraMetadata.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY);
1156 }
1157
1158 /**
1159 * Whether or not the hardware level is at least limited.
1160 *
1161 * @return {@code true} if the device is {@code LIMITED} or {@code FULL},
1162 * {@code false} otherwise (i.e. LEGACY).
1163 */
1164 private boolean isHardwareLevelAtLeastLimited() {
1165 return isHardwareLevelAtLeast(CameraMetadata.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED);
1166 }
1167
1168 /**
1169 * Whether or not the hardware level is at least full.
1170 *
1171 * @return {@code true} if the device is {@code FULL}, {@code false} otherwise.
1172 */
1173 private boolean isHardwareLevelAtLeastFull() {
1174 return isHardwareLevelAtLeast(CameraMetadata.INFO_SUPPORTED_HARDWARE_LEVEL_FULL);
1175 }
1176
1177 /**
1178 * Whether or not the hardware level is at least Level 3.
1179 *
1180 * @return {@code true} if the device is {@code LEVEL3}, {@code false} otherwise.
1181 */
1182 private boolean isHardwareLevelAtLeastLevel3() {
1183 return isHardwareLevelAtLeast(CameraMetadata.INFO_SUPPORTED_HARDWARE_LEVEL_3);
1184 }
1185
1186 /**
1187 * Determine whether the current device supports a capability or not.
1188 *
1189 * @param capability (non-negative)
1190 *
1191 * @return {@code true} if the capability is supported, {@code false} otherwise.
1192 *
1193 */
1194 private boolean isCapabilitySupported(int capability) {
1195 return mCapabilities.contains(capability);
1196 }
1197
1198 /**
1199 * Check whether the current device is backward compatible.
1200 */
1201 private boolean isColorOutputSupported() {
1202 return isCapabilitySupported(
1203 CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE);
1204 }
1205
1206 /**
Emilian Peev275da0b2018-11-15 18:20:24 +00001207 * Check whether the current device supports private reprocessing.
1208 */
1209 private boolean isPrivateReprocessingSupported() {
1210 return isCapabilitySupported(
1211 CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_PRIVATE_REPROCESSING);
1212 }
1213
1214 /**
1215 * Check whether the current device supports YUV reprocessing.
1216 */
1217 private boolean isYUVReprocessingSupported() {
1218 return isCapabilitySupported(
1219 CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_YUV_REPROCESSING);
1220 }
1221
1222 /**
Emilian Peev423cbd72018-11-10 18:37:45 +00001223 * Return the maximum supported video size using the camcorder profile information.
1224 *
1225 * @return Maximum supported video size.
1226 */
Emilian Peev0081bcf2019-02-26 14:55:58 -08001227 private @Nullable Size getMaxRecordingSize() {
Emilian Peev423cbd72018-11-10 18:37:45 +00001228 int quality =
1229 CamcorderProfile.hasProfile(mCameraId, CamcorderProfile.QUALITY_2160P) ?
1230 CamcorderProfile.QUALITY_2160P :
1231 CamcorderProfile.hasProfile(mCameraId, CamcorderProfile.QUALITY_1080P) ?
1232 CamcorderProfile.QUALITY_1080P :
1233 CamcorderProfile.hasProfile(mCameraId, CamcorderProfile.QUALITY_720P) ?
1234 CamcorderProfile.QUALITY_720P :
1235 CamcorderProfile.hasProfile(mCameraId, CamcorderProfile.QUALITY_480P) ?
1236 CamcorderProfile.QUALITY_480P :
1237 CamcorderProfile.hasProfile(mCameraId, CamcorderProfile.QUALITY_QVGA) ?
1238 CamcorderProfile.QUALITY_QVGA :
1239 CamcorderProfile.hasProfile(mCameraId, CamcorderProfile.QUALITY_CIF) ?
1240 CamcorderProfile.QUALITY_CIF :
1241 CamcorderProfile.hasProfile(mCameraId, CamcorderProfile.QUALITY_QCIF) ?
1242 CamcorderProfile.QUALITY_QCIF :
1243 -1;
1244
1245 if (quality < 0) {
1246 return null;
1247 }
1248
1249 CamcorderProfile maxProfile = CamcorderProfile.get(mCameraId, quality);
1250 return new Size(maxProfile.videoFrameWidth, maxProfile.videoFrameHeight);
1251 }
1252
1253 /**
Shuzhen Wangaae38962019-05-30 10:11:47 -07001254 * Return the maximum supported video size for cameras using data from
Emilian Peev423cbd72018-11-10 18:37:45 +00001255 * the stream configuration map.
1256 *
1257 * @return Maximum supported video size.
1258 */
Shuzhen Wangaae38962019-05-30 10:11:47 -07001259 private @NonNull Size getMaxCameraRecordingSize() {
Emilian Peev423cbd72018-11-10 18:37:45 +00001260 final Size FULLHD = new Size(1920, 1080);
1261
1262 Size[] videoSizeArr = mStreamConfigMap.getOutputSizes(
1263 android.media.MediaRecorder.class);
1264 List<Size> sizes = new ArrayList<Size>();
1265 for (Size sz: videoSizeArr) {
1266 if (sz.getWidth() <= FULLHD.getWidth() && sz.getHeight() <= FULLHD.getHeight()) {
1267 sizes.add(sz);
1268 }
1269 }
1270 List<Size> videoSizes = getAscendingOrderSizes(sizes, /*ascending*/false);
1271 for (Size sz : videoSizes) {
1272 long minFrameDuration = mStreamConfigMap.getOutputMinFrameDuration(
1273 android.media.MediaRecorder.class, sz);
1274 // Give some margin for rounding error
1275 if (minFrameDuration > (1e9 / 30.1)) {
1276 Log.i(TAG, "External camera " + mCameraId + " has max video size:" + sz);
1277 return sz;
1278 }
1279 }
1280 Log.w(TAG, "Camera " + mCameraId + " does not support any 30fps video output");
1281 return FULLHD; // doesn't matter what size is returned here
1282 }
1283
Emilian Peev0081bcf2019-02-26 14:55:58 -08001284 private @NonNull Size getMaxPreviewSize(List<Size> orderedPreviewSizes) {
Emilian Peev423cbd72018-11-10 18:37:45 +00001285 if (orderedPreviewSizes != null) {
1286 for (Size size : orderedPreviewSizes) {
1287 if ((mDisplaySize.getWidth() >= size.getWidth()) &&
Emilian Peev8649a912019-12-16 13:57:12 -08001288 (mDisplaySize.getHeight() >= size.getHeight())) {
Emilian Peev423cbd72018-11-10 18:37:45 +00001289 return size;
1290 }
1291 }
1292 }
1293
1294 Log.w(TAG,"Camera " + mCameraId + " maximum preview size search failed with "
1295 + "display size " + mDisplaySize);
1296 return kPreviewSizeBound;
1297 }
1298
1299 /**
1300 * Size comparison method used by size comparators.
1301 */
1302 private static int compareSizes(int widthA, int heightA, int widthB, int heightB) {
1303 long left = widthA * (long) heightA;
1304 long right = widthB * (long) heightB;
1305 if (left == right) {
1306 left = widthA;
1307 right = widthB;
1308 }
1309 return (left < right) ? -1 : (left > right ? 1 : 0);
1310 }
1311
1312 /**
1313 * Size comparator that compares the number of pixels it covers.
1314 *
1315 * <p>If two the areas of two sizes are same, compare the widths.</p>
1316 */
1317 public static class SizeComparator implements Comparator<Size> {
1318 @Override
Emilian Peev0081bcf2019-02-26 14:55:58 -08001319 public int compare(@NonNull Size lhs, @NonNull Size rhs) {
Emilian Peev423cbd72018-11-10 18:37:45 +00001320 return compareSizes(lhs.getWidth(), lhs.getHeight(), rhs.getWidth(),
1321 rhs.getHeight());
1322 }
1323 }
1324
1325 /**
1326 * Get a sorted list of sizes from a given size list.
1327 *
1328 * <p>
1329 * The size is compare by area it covers, if the areas are same, then
1330 * compare the widths.
1331 * </p>
1332 *
1333 * @param sizeList The input size list to be sorted
1334 * @param ascending True if the order is ascending, otherwise descending order
1335 * @return The ordered list of sizes
1336 */
Emilian Peev0081bcf2019-02-26 14:55:58 -08001337 private static @NonNull List<Size> getAscendingOrderSizes(
1338 @NonNull final List<Size> sizeList, boolean ascending) {
Emilian Peev423cbd72018-11-10 18:37:45 +00001339 Comparator<Size> comparator = new SizeComparator();
1340 List<Size> sortedSizes = new ArrayList<Size>();
1341 sortedSizes.addAll(sizeList);
1342 Collections.sort(sortedSizes, comparator);
1343 if (!ascending) {
1344 Collections.reverse(sortedSizes);
1345 }
1346
1347 return sortedSizes;
1348 }
1349 }
1350}