1 /*
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 
17 package android.hardware.camera2.params;
18 
19 import static android.hardware.camera2.params.StreamConfigurationMap.checkArgumentFormat;
20 
21 import android.annotation.NonNull;
22 import android.annotation.Nullable;
23 import android.graphics.ImageFormat;
24 import android.graphics.ImageFormat.Format;
25 import android.hardware.camera2.CameraCharacteristics;
26 import android.hardware.camera2.CameraDevice;
27 import android.hardware.camera2.CameraManager;
28 import android.hardware.camera2.CameraMetadata;
29 import android.hardware.camera2.params.OutputConfiguration;
30 import android.hardware.camera2.params.OutputConfiguration.StreamUseCase;
31 import android.hardware.camera2.params.StreamConfigurationMap;
32 import android.hardware.camera2.utils.HashCodeHelpers;
33 import android.media.CamcorderProfile;
34 import android.util.Log;
35 import android.util.Pair;
36 import android.util.Size;
37 
38 import java.util.ArrayList;
39 import java.util.Arrays;
40 import java.util.Collections;
41 import java.util.Comparator;
42 import java.util.HashMap;
43 import java.util.HashSet;
44 import java.util.List;
45 
46 /**
47  * Immutable class to store the available mandatory stream combination.
48  *
49  * <p>A mandatory stream combination refers to a specific entry in the documented sets of
50  * required stream {@link CameraDevice#createCaptureSession combinations}.
51  * These combinations of streams are required to be supported by the camera device.
52  *
53  * <p>The list of stream combinations is available by invoking
54  * {@link CameraCharacteristics#get} and passing key
55  * {@link android.hardware.camera2.CameraCharacteristics#SCALER_MANDATORY_STREAM_COMBINATIONS}.</p>
56  */
57 public final class MandatoryStreamCombination {
58     private static final String TAG = "MandatoryStreamCombination";
59     /**
60      * Immutable class to store available mandatory stream information.
61      */
62     public static final class MandatoryStreamInformation {
63         private final int mFormat;
64         private final ArrayList<Size> mAvailableSizes = new ArrayList<Size> ();
65         private final boolean mIsInput;
66         private final boolean mIsUltraHighResolution;
67         private final boolean mIsMaximumSize;
68         private final boolean mIs10BitCapable;
69         private final long mStreamUseCase;
70 
71         /**
72          * Create a new {@link MandatoryStreamInformation}.
73          *
74          * @param availableSizes List of possible stream sizes.
75          * @param format Image format.
76          * @param isMaximumSize Whether this is a maximum size stream.
77          *
78          * @throws IllegalArgumentException
79          *              if sizes is empty or if the format was not user-defined in
80          *              ImageFormat/PixelFormat.
81          * @hide
82          */
MandatoryStreamInformation(@onNull List<Size> availableSizes, @Format int format, boolean isMaximumSize)83         public MandatoryStreamInformation(@NonNull List<Size> availableSizes, @Format int format,
84                 boolean isMaximumSize) {
85             this(availableSizes, format, isMaximumSize, /*isInput*/false,
86                     /*isUltraHighResolution*/false);
87         }
88 
89         /**
90          * Create a new {@link MandatoryStreamInformation}.
91          *
92          * @param availableSizes List of possible stream sizes.
93          * @param format Image format.
94          * @param isMaximumSize Whether this is a maximum size stream.
95          * @param isInput Flag indicating whether this stream is input.
96          *
97          * @throws IllegalArgumentException
98          *              if sizes is empty or if the format was not user-defined in
99          *              ImageFormat/PixelFormat.
100          * @hide
101          */
MandatoryStreamInformation(@onNull List<Size> availableSizes, @Format int format, boolean isMaximumSize, boolean isInput)102         public MandatoryStreamInformation(@NonNull List<Size> availableSizes, @Format int format,
103                 boolean isMaximumSize, boolean isInput) {
104             this(availableSizes, format, isMaximumSize, isInput,
105                     /*isUltraHighResolution*/ false);
106         }
107 
108         /**
109          * Create a new {@link MandatoryStreamInformation}.
110          *
111          * @param availableSizes List of possible stream sizes.
112          * @param format Image format.
113          * @param isMaximumSize Whether this is a maximum size stream.
114          * @param isInput Flag indicating whether this stream is input.
115          * @param isUltraHighResolution Flag indicating whether this is a ultra-high resolution
116          *                              stream.
117          *
118          * @throws IllegalArgumentException
119          *              if sizes is empty or if the format was not user-defined in
120          *              ImageFormat/PixelFormat.
121          * @hide
122          */
MandatoryStreamInformation(@onNull List<Size> availableSizes, @Format int format, boolean isMaximumSize, boolean isInput, boolean isUltraHighResolution)123         public MandatoryStreamInformation(@NonNull List<Size> availableSizes, @Format int format,
124                 boolean isMaximumSize, boolean isInput, boolean isUltraHighResolution) {
125             this(availableSizes, format, isMaximumSize, isInput, isUltraHighResolution,
126                     /*is10bitCapable*/ false);
127         }
128 
129         /**
130          * Create a new {@link MandatoryStreamInformation}.
131          *
132          * @param availableSizes List of possible stream sizes.
133          * @param format Image format.
134          * @param isMaximumSize Whether this is a maximum size stream.
135          * @param isInput Flag indicating whether this stream is input.
136          * @param isUltraHighResolution Flag indicating whether this is a ultra-high resolution
137          *                              stream.
138          * @param is10BitCapable Flag indicating whether this stream is able to support 10-bit
139          *
140          * @throws IllegalArgumentException
141          *              if sizes is empty or if the format was not user-defined in
142          *              ImageFormat/PixelFormat.
143          * @hide
144          */
MandatoryStreamInformation(@onNull List<Size> availableSizes, @Format int format, boolean isMaximumSize, boolean isInput, boolean isUltraHighResolution, boolean is10BitCapable)145         public MandatoryStreamInformation(@NonNull List<Size> availableSizes, @Format int format,
146                 boolean isMaximumSize, boolean isInput, boolean isUltraHighResolution,
147                 boolean is10BitCapable) {
148             this(availableSizes, format, isMaximumSize, isInput, isUltraHighResolution,
149                     is10BitCapable, CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_DEFAULT);
150         }
151 
152         /**
153          * Create a new {@link MandatoryStreamInformation}.
154          *
155          * @param availableSizes List of possible stream sizes.
156          * @param format Image format.
157          * @param isMaximumSize Whether this is a maximum size stream.
158          * @param isInput Flag indicating whether this stream is input.
159          * @param isUltraHighResolution Flag indicating whether this is a ultra-high resolution
160          *                              stream.
161          * @param is10BitCapable Flag indicating whether this stream is able to support 10-bit
162          * @param streamUseCase The stream use case.
163          *
164          * @throws IllegalArgumentException
165          *              if sizes is empty or if the format was not user-defined in
166          *              ImageFormat/PixelFormat.
167          * @hide
168          */
MandatoryStreamInformation(@onNull List<Size> availableSizes, @Format int format, boolean isMaximumSize, boolean isInput, boolean isUltraHighResolution, boolean is10BitCapable, @StreamUseCase long streamUseCase)169         public MandatoryStreamInformation(@NonNull List<Size> availableSizes, @Format int format,
170                 boolean isMaximumSize, boolean isInput, boolean isUltraHighResolution,
171                 boolean is10BitCapable, @StreamUseCase long streamUseCase) {
172             if (availableSizes.isEmpty()) {
173                 throw new IllegalArgumentException("No available sizes");
174             }
175             mAvailableSizes.addAll(availableSizes);
176             mFormat = checkArgumentFormat(format);
177             mIsMaximumSize = isMaximumSize;
178             mIsInput = isInput;
179             mIsUltraHighResolution = isUltraHighResolution;
180             mIs10BitCapable = is10BitCapable;
181             mStreamUseCase = streamUseCase;
182         }
183 
184         /**
185          * Confirms whether or not this is an input stream.
186          * @return true in case the stream is input, false otherwise.
187          */
isInput()188         public boolean isInput() {
189             return mIsInput;
190         }
191 
192         /**
193          * Confirms whether or not this is an ultra high resolution stream.
194          *
195          * <p>An 'ultra high resolution' stream is one which has a configuration which appears in
196          * {@link android.hardware.camera2.CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP_MAXIMUM_RESOLUTION},
197          * Streams which are ultra high resolution must not be included with streams which are not
198          * ultra high resolution in the same {@link android.hardware.camera2.CaptureRequest}.</p>
199          *
200          * @return true in case the stream is ultra high resolution, false otherwise.
201         */
isUltraHighResolution()202         public boolean isUltraHighResolution() {
203             return mIsUltraHighResolution;
204         }
205 
206         /**
207          * Confirms whether or not this is a maximum size stream.
208          *
209          * <p>A stream with maximum size is one with the camera device's maximum resolution
210          * for the stream's format as appears in {@link
211          * android.hardware.camera2.CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP}. This
212          * maximum size has the same meaning as the 'MAXIMUM' target size documented in the camera
213          * capture session {@link CameraDevice#createCaptureSession guideline}.</p>
214          *
215          * <p>The application can use a
216          * {@link android.hardware.camera2.MultiResolutionImageReader} for a maximum size
217          * output stream if the camera device supports multi-resolution outputs for the stream's
218          * format. See {@link
219          * android.hardware.camera2.CameraCharacteristics#SCALER_MULTI_RESOLUTION_STREAM_CONFIGURATION_MAP}
220          * for details.</p>
221          *
222          * <p>This is different from the ultra high resolution flag, which applies only to
223          * ultra high resolution sensor camera devices and refers to a stream in
224          * {@link
225          * android.hardware.camera2.CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP_MAXIMUM_RESOLUTION}
226          * instead.</p>
227          *
228          * @return true if the stream is a maximum size stream.
229          */
isMaximumSize()230         public boolean isMaximumSize() {
231             return mIsMaximumSize;
232         }
233 
234         /**
235          * Indicates whether this stream is able to support 10-bit output.
236          *
237          * <p>10-bit capable streams can be configured to output 10-bit sample data via calls to
238          * {@link android.hardware.camera2.params.OutputConfiguration#setDynamicRangeProfile} and
239          * selecting the appropriate output Surface pixel format which can be queried via
240          * {@link #get10BitFormat()} and will be either
241          * {@link ImageFormat#PRIVATE} (the default for Surfaces initialized by
242          * {@link android.view.SurfaceView}, {@link android.view.TextureView},
243          * {@link android.media.MediaRecorder}, {@link android.media.MediaCodec} etc.) or
244          * {@link ImageFormat#YCBCR_P010}.</p>
245          *
246          * @return true if stream is able to output 10-bit pixels
247          *
248          * @see android.hardware.camera2.CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_DYNAMIC_RANGE_TEN_BIT
249          * @see OutputConfiguration#setDynamicRangeProfile
250          */
is10BitCapable()251         public boolean is10BitCapable() {
252             return mIs10BitCapable;
253         }
254 
255         /**
256          * Return the list of available sizes for this mandatory stream.
257          *
258          * <p>Per documented {@link CameraDevice#createCaptureSession guideline} the largest
259          * resolution in the result will be tested and guaranteed to work. If clients want to use
260          * smaller sizes, then the resulting
261          * {@link android.hardware.camera2.params.SessionConfiguration session configuration} can
262          * be tested either by calling {@link CameraDevice#createCaptureSession} or
263          * {@link CameraDevice.CameraDeviceSetup#isSessionConfigurationSupported}.
264          *
265          * @return non-modifiable ascending list of available sizes.
266          */
getAvailableSizes()267         public @NonNull List<Size> getAvailableSizes() {
268             return Collections.unmodifiableList(mAvailableSizes);
269         }
270 
271         /**
272          * Retrieve the mandatory stream {@code format}.
273          *
274          * @return integer format.
275          */
getFormat()276         public @Format int getFormat() {
277             // P010 YUV streams must be supported along with SDR 8-bit YUV streams
278             if ((mIs10BitCapable)  && (mFormat == ImageFormat.YCBCR_P010)) {
279                 return ImageFormat.YUV_420_888;
280             }
281             return mFormat;
282         }
283 
284         /**
285          * Retrieve the mandatory stream 10-bit {@code format} for 10-bit capable streams.
286          *
287          * <p>In case {@link #is10BitCapable()} returns {@code true}, then this method
288          * will return the corresponding 10-bit output Surface pixel format. Depending on
289          * the stream type it will be either {@link ImageFormat#PRIVATE} or
290          * {@link ImageFormat#YCBCR_P010}.</p>
291          *
292          * @return integer format.
293          * @throws UnsupportedOperationException in case the stream is not capable of 10-bit output
294          * @see #is10BitCapable()
295          */
get10BitFormat()296         public @Format int get10BitFormat() {
297             if (!mIs10BitCapable) {
298                 throw new UnsupportedOperationException("10-bit output is not supported!");
299             }
300             return mFormat;
301         }
302 
303         /**
304          * Retrieve the mandatory stream use case.
305          *
306          * <p>If this {@link MandatoryStreamInformation} is part of a mandatory stream
307          * combination for stream use cases, the return value will be a non-DEFAULT value.
308          * For {@link MandatoryStreamInformation} belonging to other mandatory stream
309          * combinations, the return value will be DEFAULT. </p>
310          *
311          * @return the long integer stream use case.
312          */
getStreamUseCase()313         public @StreamUseCase long getStreamUseCase() {
314             return mStreamUseCase;
315         }
316 
317         /**
318          * Check if this {@link MandatoryStreamInformation} is equal to another
319          * {@link MandatoryStreamInformation}.
320          *
321          * <p>Two vectors are only equal if and only if each of the respective elements is
322          * equal.</p>
323          *
324          * @return {@code true} if the objects were equal, {@code false} otherwise
325          */
326         @Override
equals(final Object obj)327         public boolean equals(final Object obj) {
328             if (obj == null) {
329                 return false;
330             }
331             if (this == obj) {
332                 return true;
333             }
334             if (obj instanceof MandatoryStreamInformation) {
335                 final MandatoryStreamInformation other = (MandatoryStreamInformation) obj;
336                 if ((mFormat != other.mFormat) || (mIsInput != other.mIsInput) ||
337                         (mIsUltraHighResolution != other.mIsUltraHighResolution) ||
338                         (mStreamUseCase != other.mStreamUseCase) ||
339                         (mAvailableSizes.size() != other.mAvailableSizes.size())) {
340                     return false;
341                 }
342 
343                 return mAvailableSizes.equals(other.mAvailableSizes);
344             }
345 
346             return false;
347         }
348 
349         /**
350          * {@inheritDoc}
351          */
352         @Override
hashCode()353         public int hashCode() {
354             return HashCodeHelpers.hashCode(mFormat, Boolean.hashCode(mIsInput),
355                     Boolean.hashCode(mIsUltraHighResolution), mAvailableSizes.hashCode(),
356                     mStreamUseCase);
357         }
358     }
359 
360     private final String mDescription;
361     private final boolean mIsReprocessable;
362     private final ArrayList<MandatoryStreamInformation> mStreamsInformation =
363             new ArrayList<MandatoryStreamInformation>();
364 
365     /**
366      * Short hand for stream use cases
367      */
368     private static final long STREAM_USE_CASE_PREVIEW =
369             CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_PREVIEW;
370     private static final long STREAM_USE_CASE_STILL_CAPTURE =
371             CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_STILL_CAPTURE;
372     private static final long STREAM_USE_CASE_RECORD =
373             CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_VIDEO_RECORD;
374     private static final long STREAM_USE_CASE_PREVIEW_VIDEO_STILL =
375             CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_PREVIEW_VIDEO_STILL;
376     private static final long STREAM_USE_CASE_VIDEO_CALL =
377             CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_VIDEO_CALL;
378     private static final long STREAM_USE_CASE_CROPPED_RAW =
379             CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_CROPPED_RAW;
380 
381     /**
382      * Create a new {@link MandatoryStreamCombination}.
383      *
384      * @param streamsInformation list of available streams in the stream combination.
385      * @param description Summary of the stream combination use case.
386      * @param isReprocessable Flag whether the mandatory stream combination is reprocessable.
387      *
388      * @throws IllegalArgumentException
389      *              if stream information is empty
390      * @hide
391      */
MandatoryStreamCombination(@onNull List<MandatoryStreamInformation> streamsInformation, @NonNull String description, boolean isReprocessable)392     public MandatoryStreamCombination(@NonNull List<MandatoryStreamInformation> streamsInformation,
393             @NonNull String description, boolean isReprocessable) {
394         if (streamsInformation.isEmpty()) {
395             throw new IllegalArgumentException("Empty stream information");
396         }
397         mStreamsInformation.addAll(streamsInformation);
398         mDescription = description;
399         mIsReprocessable = isReprocessable;
400     }
401     /**
402      * Get the mandatory stream combination description.
403      *
404      * @return CharSequence with the mandatory combination description.
405      */
getDescription()406     public @NonNull CharSequence getDescription() {
407         return mDescription;
408     }
409 
410     /**
411      * Indicates whether the mandatory stream combination is reprocessable. Reprocessable is defined
412      * as a stream combination that contains one input stream
413      * ({@link MandatoryStreamInformation#isInput} return true).
414      *
415      * @return {@code true} in case the mandatory stream combination contains an input,
416      *         {@code false} otherwise.
417      */
isReprocessable()418     public boolean isReprocessable() {
419         return mIsReprocessable;
420     }
421 
422     /**
423      * Get information about each stream in the mandatory combination.
424      *
425      * @return Non-modifiable list of stream information.
426      *
427      */
getStreamsInformation()428     public @NonNull List<MandatoryStreamInformation> getStreamsInformation() {
429         return Collections.unmodifiableList(mStreamsInformation);
430     }
431 
432     /**
433      * Check if this {@link MandatoryStreamCombination} is equal to another
434      * {@link MandatoryStreamCombination}.
435      *
436      * <p>Two vectors are only equal if and only if each of the respective elements is equal.</p>
437      *
438      * @return {@code true} if the objects were equal, {@code false} otherwise
439      */
440     @Override
equals(final Object obj)441     public boolean equals(final Object obj) {
442         if (obj == null) {
443             return false;
444         }
445         if (this == obj) {
446             return true;
447         }
448         if (obj instanceof MandatoryStreamCombination) {
449             final MandatoryStreamCombination other = (MandatoryStreamCombination) obj;
450             if ((mDescription != other.mDescription) ||
451                     (mIsReprocessable != other.mIsReprocessable) ||
452                     (mStreamsInformation.size() != other.mStreamsInformation.size())) {
453                 return false;
454             }
455 
456             return mStreamsInformation.equals(other.mStreamsInformation);
457         }
458 
459         return false;
460     }
461 
462     /**
463      * {@inheritDoc}
464      */
465     @Override
hashCode()466     public int hashCode() {
467         return HashCodeHelpers.hashCode(Boolean.hashCode(mIsReprocessable), mDescription.hashCode(),
468                 mStreamsInformation.hashCode());
469     }
470 
471     private static enum SizeThreshold { VGA, PREVIEW, RECORD, MAXIMUM, s720p, s1440p, FULL_RES }
472     private static enum ReprocessType { NONE, PRIVATE, YUV, REMOSAIC }
473     private static final class StreamTemplate {
474         public int mFormat;
475         public SizeThreshold mSizeThreshold;
476         public long mStreamUseCase;
StreamTemplate(int format, SizeThreshold sizeThreshold)477         public StreamTemplate(int format, SizeThreshold sizeThreshold) {
478             this(format, sizeThreshold, CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_DEFAULT);
479         }
StreamTemplate(@ormat int format, @NonNull SizeThreshold sizeThreshold, @StreamUseCase long streamUseCase)480         public StreamTemplate(@Format int format, @NonNull SizeThreshold sizeThreshold,
481                 @StreamUseCase long streamUseCase) {
482             mFormat = format;
483             mSizeThreshold = sizeThreshold;
484             mStreamUseCase = streamUseCase;
485         }
486     }
487 
488     private static final class StreamCombinationTemplate {
489         public StreamTemplate[] mStreamTemplates;
490         public String mDescription;
491         public ReprocessType mReprocessType;
492         // Substitute MAXIMUM size YUV output stream with JPEG / RAW_SENSOR.
493         public boolean mSubstituteYUV = false;
494 
StreamCombinationTemplate(@onNull StreamTemplate[] streamTemplates, @NonNull String description)495         public StreamCombinationTemplate(@NonNull StreamTemplate[] streamTemplates,
496                 @NonNull String description) {
497             this(streamTemplates, description, /*reprocessType*/ReprocessType.NONE);
498         }
499 
StreamCombinationTemplate(@onNull StreamTemplate[] streamTemplates, @NonNull String description, ReprocessType reprocessType)500         public StreamCombinationTemplate(@NonNull StreamTemplate[] streamTemplates,
501                 @NonNull String description, ReprocessType reprocessType) {
502             this(streamTemplates, description, reprocessType, /*substituteYUV*/ false);
503         }
504 
StreamCombinationTemplate(@onNull StreamTemplate[] streamTemplates, @NonNull String description, boolean substituteYUV)505         public StreamCombinationTemplate(@NonNull StreamTemplate[] streamTemplates,
506                 @NonNull String description, boolean substituteYUV) {
507             this(streamTemplates, description, /*reprocessType*/ ReprocessType.NONE,
508                     substituteYUV);
509         }
510 
StreamCombinationTemplate(@onNull StreamTemplate[] streamTemplates, @NonNull String description, ReprocessType reprocessType, boolean substituteYUV)511         public StreamCombinationTemplate(@NonNull StreamTemplate[] streamTemplates,
512                 @NonNull String description, ReprocessType reprocessType, boolean substituteYUV) {
513             mStreamTemplates = streamTemplates;
514             mReprocessType = reprocessType;
515             mDescription = description;
516             mSubstituteYUV = substituteYUV;
517         }
518     }
519 
520     private static StreamCombinationTemplate sLegacyCombinations[] = {
521         new StreamCombinationTemplate(new StreamTemplate [] {
522                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.MAXIMUM) },
523                 "Simple preview, GPU video processing, or no-preview video recording"),
524         new StreamCombinationTemplate(new StreamTemplate [] {
525                 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM) },
526                 "No-viewfinder still image capture"),
527         new StreamCombinationTemplate(new StreamTemplate [] {
528                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.MAXIMUM) },
529                 "In-application video/image processing"),
530         new StreamCombinationTemplate(new StreamTemplate [] {
531                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
532                 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM) },
533                 "Standard still imaging"),
534         new StreamCombinationTemplate(new StreamTemplate [] {
535                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
536                 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM) },
537                 "In-app processing plus still capture"),
538         new StreamCombinationTemplate(new StreamTemplate [] {
539                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
540                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW) },
541                 "Standard recording"),
542         new StreamCombinationTemplate(new StreamTemplate [] {
543                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
544                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW) },
545                 "Preview plus in-app processing"),
546         new StreamCombinationTemplate(new StreamTemplate [] {
547                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
548                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
549                 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM) },
550                 "Still capture plus in-app processing")
551     };
552 
553     private static StreamCombinationTemplate sLimitedCombinations[] = {
554         new StreamCombinationTemplate(new StreamTemplate [] {
555                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
556                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.RECORD)},
557                 "High-resolution video recording with preview"),
558         new StreamCombinationTemplate(new StreamTemplate [] {
559                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
560                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.RECORD)},
561                 "High-resolution in-app video processing with preview"),
562         new StreamCombinationTemplate(new StreamTemplate [] {
563                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
564                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.RECORD) },
565                 "Two-input in-app video processing"),
566         new StreamCombinationTemplate(new StreamTemplate [] {
567                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
568                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.RECORD),
569                 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.RECORD) },
570                 "High-resolution recording with video snapshot"),
571         new StreamCombinationTemplate(new StreamTemplate [] {
572                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
573                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.RECORD),
574                 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.RECORD) },
575                 "High-resolution in-app processing with video snapshot"),
576         new StreamCombinationTemplate(new StreamTemplate [] {
577                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
578                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
579                 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM) },
580                 "Two-input in-app processing with still capture")
581     };
582 
583     private static StreamCombinationTemplate sBurstCombinations[] = {
584         new StreamCombinationTemplate(new StreamTemplate [] {
585                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
586                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.MAXIMUM) },
587                 "Maximum-resolution GPU processing with preview"),
588         new StreamCombinationTemplate(new StreamTemplate [] {
589                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
590                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.MAXIMUM) },
591                 "Maximum-resolution in-app processing with preview"),
592         new StreamCombinationTemplate(new StreamTemplate [] {
593                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
594                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.MAXIMUM) },
595                 "Maximum-resolution two-input in-app processsing")
596     };
597 
598     private static StreamCombinationTemplate sFullCombinations[] = {
599         new StreamCombinationTemplate(new StreamTemplate [] {
600                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.MAXIMUM),
601                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.MAXIMUM) },
602                 "Maximum-resolution GPU processing with preview"),
603         new StreamCombinationTemplate(new StreamTemplate [] {
604                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.MAXIMUM),
605                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.MAXIMUM) },
606                 "Maximum-resolution in-app processing with preview"),
607         new StreamCombinationTemplate(new StreamTemplate [] {
608                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.MAXIMUM),
609                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.MAXIMUM) },
610                 "Maximum-resolution two-input in-app processing"),
611         new StreamCombinationTemplate(new StreamTemplate [] {
612                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
613                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
614                 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM) },
615                 "Video recording with maximum-size video snapshot"),
616         new StreamCombinationTemplate(new StreamTemplate [] {
617                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.VGA),
618                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
619                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.MAXIMUM) },
620                 "Standard video recording plus maximum-resolution in-app processing"),
621         new StreamCombinationTemplate(new StreamTemplate [] {
622                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.VGA),
623                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
624                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.MAXIMUM) },
625                 "Preview plus two-input maximum-resolution in-app processing")
626     };
627 
628     private static StreamCombinationTemplate sRawCombinations[] = {
629         new StreamCombinationTemplate(new StreamTemplate [] {
630                 new StreamTemplate(ImageFormat.RAW_SENSOR,  SizeThreshold.MAXIMUM) },
631                 "No-preview DNG capture"),
632         new StreamCombinationTemplate(new StreamTemplate [] {
633                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
634                 new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.MAXIMUM) },
635                 "Standard DNG capture"),
636         new StreamCombinationTemplate(new StreamTemplate [] {
637                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
638                 new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.MAXIMUM) },
639                 "In-app processing plus DNG capture"),
640         new StreamCombinationTemplate(new StreamTemplate [] {
641                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
642                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
643                 new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.MAXIMUM) },
644                 "Video recording with DNG capture"),
645         new StreamCombinationTemplate(new StreamTemplate [] {
646                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
647                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
648                 new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.MAXIMUM) },
649                 "Preview with in-app processing and DNG capture"),
650         new StreamCombinationTemplate(new StreamTemplate [] {
651                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
652                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
653                 new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.MAXIMUM) },
654                 "Two-input in-app processing plus DNG capture"),
655         new StreamCombinationTemplate(new StreamTemplate [] {
656                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
657                 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM),
658                 new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.MAXIMUM) },
659                 "Still capture with simultaneous JPEG and DNG"),
660         new StreamCombinationTemplate(new StreamTemplate [] {
661                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
662                 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM),
663                 new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.MAXIMUM) },
664                 "In-app processing with simultaneous JPEG and DNG")
665     };
666 
667     private static StreamCombinationTemplate sLevel3Combinations[] = {
668         new StreamCombinationTemplate(new StreamTemplate [] {
669                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
670                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.VGA),
671                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.MAXIMUM),
672                 new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.MAXIMUM) },
673                 "In-app viewfinder analysis with dynamic selection of output format"),
674         new StreamCombinationTemplate(new StreamTemplate [] {
675                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
676                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.VGA),
677                 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM),
678                 new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.MAXIMUM) },
679                 "In-app viewfinder analysis with dynamic selection of output format")
680     };
681 
682     private static StreamCombinationTemplate sLimitedPrivateReprocCombinations[] = {
683         new StreamCombinationTemplate(new StreamTemplate [] {
684                 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM) },
685                 "No-viewfinder still image reprocessing",
686                 /*reprocessType*/ ReprocessType.PRIVATE),
687         new StreamCombinationTemplate(new StreamTemplate [] {
688                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
689                 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM) },
690                 "ZSL(Zero-Shutter-Lag) still imaging",
691                 /*reprocessType*/ ReprocessType.PRIVATE),
692         new StreamCombinationTemplate(new StreamTemplate [] {
693                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
694                 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM) },
695                 "ZSL still and in-app processing imaging",
696                 /*reprocessType*/ ReprocessType.PRIVATE),
697         new StreamCombinationTemplate(new StreamTemplate [] {
698                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
699                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
700                 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM) },
701                 "ZSL in-app processing with still capture",
702                 /*reprocessType*/ ReprocessType.PRIVATE),
703     };
704 
705     private static StreamCombinationTemplate sLimitedYUVReprocCombinations[] = {
706         new StreamCombinationTemplate(new StreamTemplate [] {
707                 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM) },
708                 "No-viewfinder still image reprocessing",
709                 /*reprocessType*/ ReprocessType.YUV),
710         new StreamCombinationTemplate(new StreamTemplate [] {
711                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
712                 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM) },
713                 "ZSL(Zero-Shutter-Lag) still imaging",
714                 /*reprocessType*/ ReprocessType.YUV),
715         new StreamCombinationTemplate(new StreamTemplate [] {
716                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
717                 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM) },
718                 "ZSL still and in-app processing imaging",
719                 /*reprocessType*/ ReprocessType.YUV),
720         new StreamCombinationTemplate(new StreamTemplate [] {
721                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
722                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
723                 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM) },
724                 "ZSL in-app processing with still capture",
725                 /*reprocessType*/ ReprocessType.YUV),
726     };
727 
728     private static StreamCombinationTemplate sFullPrivateReprocCombinations[] = {
729         new StreamCombinationTemplate(new StreamTemplate [] {
730                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
731                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.RECORD) },
732                 "High-resolution ZSL in-app video processing with regular preview",
733                 /*reprocessType*/ ReprocessType.PRIVATE),
734         new StreamCombinationTemplate(new StreamTemplate [] {
735                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
736                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.MAXIMUM) },
737                 "Maximum-resolution ZSL in-app processing with regular preview",
738                 /*reprocessType*/ ReprocessType.PRIVATE),
739         new StreamCombinationTemplate(new StreamTemplate [] {
740                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
741                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.MAXIMUM) },
742                 "Maximum-resolution two-input ZSL in-app processing",
743                 /*reprocessType*/ ReprocessType.PRIVATE),
744         new StreamCombinationTemplate(new StreamTemplate [] {
745                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
746                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
747                 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM) },
748                 "ZSL still capture and in-app processing",
749                 /*reprocessType*/ ReprocessType.PRIVATE),
750     };
751 
752     private static StreamCombinationTemplate sFullYUVReprocCombinations[] = {
753         new StreamCombinationTemplate(new StreamTemplate [] {
754                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW) },
755                 "Maximum-resolution multi-frame image fusion in-app processing with regular "
756                 + "preview",
757                 /*reprocessType*/ ReprocessType.YUV),
758         new StreamCombinationTemplate(new StreamTemplate [] {
759                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW) },
760                 "Maximum-resolution multi-frame image fusion two-input in-app processing",
761                 /*reprocessType*/ ReprocessType.YUV),
762         new StreamCombinationTemplate(new StreamTemplate [] {
763                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
764                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.RECORD) },
765                 "High-resolution ZSL in-app video processing with regular preview",
766                 /*reprocessType*/ ReprocessType.YUV),
767         new StreamCombinationTemplate(new StreamTemplate [] {
768                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
769                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
770                 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM) },
771                 "ZSL still capture and in-app processing",
772                 /*reprocessType*/ ReprocessType.YUV),
773     };
774 
775     private static StreamCombinationTemplate sRAWPrivateReprocCombinations[] = {
776         new StreamCombinationTemplate(new StreamTemplate [] {
777                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
778                 new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.MAXIMUM) },
779                 "Mutually exclusive ZSL in-app processing and DNG capture",
780                 /*reprocessType*/ ReprocessType.PRIVATE),
781         new StreamCombinationTemplate(new StreamTemplate [] {
782                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
783                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
784                 new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.MAXIMUM) },
785                 "Mutually exclusive ZSL in-app processing and preview with DNG capture",
786                 /*reprocessType*/ ReprocessType.PRIVATE),
787         new StreamCombinationTemplate(new StreamTemplate [] {
788                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
789                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
790                 new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.MAXIMUM) },
791                 "Mutually exclusive ZSL two-input in-app processing and DNG capture",
792                 /*reprocessType*/ ReprocessType.PRIVATE),
793         new StreamCombinationTemplate(new StreamTemplate [] {
794                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
795                 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM),
796                 new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.MAXIMUM) },
797                 "Mutually exclusive ZSL still capture and preview with DNG capture",
798                 /*reprocessType*/ ReprocessType.PRIVATE),
799         new StreamCombinationTemplate(new StreamTemplate [] {
800                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
801                 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM),
802                 new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.MAXIMUM) },
803                 "Mutually exclusive ZSL in-app processing with still capture and DNG capture",
804                 /*reprocessType*/ ReprocessType.PRIVATE),
805     };
806 
807     private static StreamCombinationTemplate sRAWYUVReprocCombinations[] = {
808         new StreamCombinationTemplate(new StreamTemplate [] {
809                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
810                 new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.MAXIMUM) },
811                 "Mutually exclusive ZSL in-app processing and DNG capture",
812                 /*reprocessType*/ ReprocessType.YUV),
813         new StreamCombinationTemplate(new StreamTemplate [] {
814                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
815                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
816                 new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.MAXIMUM) },
817                 "Mutually exclusive ZSL in-app processing and preview with DNG capture",
818                 /*reprocessType*/ ReprocessType.YUV),
819         new StreamCombinationTemplate(new StreamTemplate [] {
820                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
821                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
822                 new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.MAXIMUM) },
823                 "Mutually exclusive ZSL two-input in-app processing and DNG capture",
824                 /*reprocessType*/ ReprocessType.YUV),
825         new StreamCombinationTemplate(new StreamTemplate [] {
826                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
827                 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM),
828                 new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.MAXIMUM) },
829                 "Mutually exclusive ZSL still capture and preview with DNG capture",
830                 /*reprocessType*/ ReprocessType.YUV),
831         new StreamCombinationTemplate(new StreamTemplate [] {
832                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
833                 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM),
834                 new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.MAXIMUM) },
835                 "Mutually exclusive ZSL in-app processing with still capture and DNG capture",
836                 /*reprocessType*/ ReprocessType.YUV),
837     };
838 
839     private static StreamCombinationTemplate sLevel3PrivateReprocCombinations[] = {
840         new StreamCombinationTemplate(new StreamTemplate [] {
841                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
842                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.VGA),
843                 new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.MAXIMUM),
844                 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM) },
845                 "In-app viewfinder analysis with ZSL, RAW, and JPEG reprocessing output",
846                 /*reprocessType*/ ReprocessType.PRIVATE),
847     };
848 
849     private static StreamCombinationTemplate sLevel3YUVReprocCombinations[] = {
850         new StreamCombinationTemplate(new StreamTemplate [] {
851                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
852                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.VGA),
853                 new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.MAXIMUM) },
854                 "In-app viewfinder analysis with ZSL and RAW",
855                 /*reprocessType*/ ReprocessType.YUV),
856         new StreamCombinationTemplate(new StreamTemplate [] {
857                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
858                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.VGA),
859                 new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.MAXIMUM),
860                 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM) },
861                 "In-app viewfinder analysis with ZSL, RAW, and JPEG reprocessing output",
862                 /*reprocessType*/ ReprocessType.YUV),
863     };
864 
865     private static StreamCombinationTemplate sConcurrentStreamCombinations[] = {
866         new StreamCombinationTemplate(new StreamTemplate [] {
867                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.s1440p) },
868                 "In-app video / image processing"),
869         new StreamCombinationTemplate(new StreamTemplate [] {
870                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.s1440p) },
871                 "preview / preview to GPU"),
872         new StreamCombinationTemplate(new StreamTemplate [] {
873                 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.s1440p) },
874                 "No view-finder still image capture"),
875         new StreamCombinationTemplate(new StreamTemplate [] {
876                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.s720p),
877                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.s1440p)},
878                 "Two-input in app video / image processing"),
879         new StreamCombinationTemplate(new StreamTemplate [] {
880                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.s720p),
881                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.s1440p)},
882                 "High resolution video recording with preview"),
883         new StreamCombinationTemplate(new StreamTemplate [] {
884                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.s720p),
885                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.s1440p)},
886                 "In-app video / image processing with preview"),
887         new StreamCombinationTemplate(new StreamTemplate [] {
888                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.s720p),
889                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.s1440p)},
890                 "In-app video / image processing with preview"),
891         new StreamCombinationTemplate(new StreamTemplate [] {
892                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.s720p),
893                 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.s1440p)},
894                 "Standard still image capture"),
895         new StreamCombinationTemplate(new StreamTemplate [] {
896                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.s720p),
897                 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.s1440p)},
898                 "Standard still image capture"),
899     };
900 
901     private static StreamCombinationTemplate sConcurrentDepthOnlyStreamCombinations[] = {
902         new StreamCombinationTemplate(new StreamTemplate [] {
903                 new StreamTemplate(ImageFormat.DEPTH16, SizeThreshold.VGA) },
904                 "Depth capture for mesh based object rendering"),
905     };
906 
907     private static StreamCombinationTemplate sUltraHighResolutionStreamCombinations[] = {
908         // UH res YUV / RAW / JPEG + PRIV preview size stream
909         new StreamCombinationTemplate(new StreamTemplate [] {
910                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.FULL_RES),
911                  new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW)},
912                 "Ultra high resolution YUV image capture with preview"),
913         new StreamCombinationTemplate(new StreamTemplate [] {
914                 new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.FULL_RES),
915                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW)},
916                 "Ultra high resolution RAW_SENSOR image capture with preview"),
917         new StreamCombinationTemplate(new StreamTemplate [] {
918                 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.FULL_RES),
919                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW)},
920                 "Ultra high resolution JPEG image capture with preview"),
921 
922         // UH res YUV / RAW / JPEG + YUV preview size stream
923         new StreamCombinationTemplate(new StreamTemplate [] {
924                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.FULL_RES),
925                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW)},
926                 "No-viewfinder Ultra high resolution YUV image capture with image analysis"),
927         new StreamCombinationTemplate(new StreamTemplate [] {
928                 new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.FULL_RES),
929                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW)},
930                 "No-viewfinder Ultra high resolution RAW_SENSOR image capture with image analysis"),
931         new StreamCombinationTemplate(new StreamTemplate [] {
932                 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.FULL_RES),
933                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW)},
934                 "No-viewfinder Ultra high resolution JPEG image capture with image analysis"),
935 
936         // UH res YUV / RAW / JPEG + PRIV preview + PRIV RECORD stream
937         new StreamCombinationTemplate(new StreamTemplate [] {
938                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.FULL_RES),
939                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
940                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.RECORD)},
941                 "Ultra high resolution YUV image capture with preview + app-based image analysis"),
942         new StreamCombinationTemplate(new StreamTemplate [] {
943                 new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.FULL_RES),
944                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
945                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.RECORD)},
946                 "Ultra high resolution RAW image capture with preview + app-based image analysis"),
947         new StreamCombinationTemplate(new StreamTemplate [] {
948                 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.FULL_RES),
949                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
950                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.RECORD)},
951                 "Ultra high resolution JPEG image capture with preview + app-based image analysis"),
952 
953         // UH res YUV / RAW / JPEG + PRIV preview + YUV RECORD stream
954         new StreamCombinationTemplate(new StreamTemplate [] {
955                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.FULL_RES),
956                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
957                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.RECORD)},
958                 "Ultra high resolution YUV image capture with preview + app-based image analysis"),
959         new StreamCombinationTemplate(new StreamTemplate [] {
960                 new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.FULL_RES),
961                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
962                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.RECORD)},
963                 "Ultra high resolution RAW image capture with preview + app-based image analysis"),
964         new StreamCombinationTemplate(new StreamTemplate [] {
965                 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.FULL_RES),
966                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
967                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.RECORD)},
968                 "Ultra high resolution JPEG image capture with preview + app-based image analysis"),
969 
970         // UH RES YUV / RAW / JPEG + PRIV preview + YUV / RAW / JPEG Maximum stream
971         new StreamCombinationTemplate(new StreamTemplate [] {
972                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.FULL_RES),
973                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
974                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.MAXIMUM)},
975                 "Ultra high resolution YUV image capture with preview + default",
976                 /*substituteYUV*/ true),
977         new StreamCombinationTemplate(new StreamTemplate [] {
978                 new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.FULL_RES),
979                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
980                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.MAXIMUM)},
981                 "Ultra high resolution RAW image capture with preview + default",
982                 /*substituteYUV*/ true),
983         new StreamCombinationTemplate(new StreamTemplate [] {
984                 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.FULL_RES),
985                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
986                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.MAXIMUM)},
987                 "Ultra high resolution JPEG capture with preview + default",
988                 /*substituteYUV*/ true),
989     };
990 
991     private static StreamCombinationTemplate sUltraHighResolutionReprocStreamCombinations[] = {
992         // RAW_SENSOR -> RAW_SENSOR + preview size PRIV / YUV
993         new StreamCombinationTemplate(new StreamTemplate [] {
994                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW)},
995                 "In-app RAW remosaic reprocessing with separate preview",
996                 /*reprocessType*/ ReprocessType.REMOSAIC),
997         new StreamCombinationTemplate(new StreamTemplate [] {
998                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW)},
999                 "In-app RAW remosaic reprocessing with in-app image analysis",
1000                 /*reprocessType*/ ReprocessType.REMOSAIC),
1001 
1002         // RAW -> JPEG / YUV reprocessing + YUV / PRIV preview size stream
1003         new StreamCombinationTemplate(new StreamTemplate [] {
1004                 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.FULL_RES),
1005                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW)},
1006                 "In-app RAW -> JPEG reprocessing with separate preview",
1007                 /*reprocessType*/ ReprocessType.REMOSAIC),
1008         new StreamCombinationTemplate(new StreamTemplate [] {
1009                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.FULL_RES),
1010                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW)},
1011                 "In-app RAW -> YUV reprocessing with separate preview",
1012                 /*reprocessType*/ ReprocessType.REMOSAIC),
1013         new StreamCombinationTemplate(new StreamTemplate [] {
1014                 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.FULL_RES),
1015                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW)},
1016                 "In-app RAW -> JPEG reprocessing with in-app image analysis",
1017                 /*reprocessType*/ ReprocessType.REMOSAIC),
1018         new StreamCombinationTemplate(new StreamTemplate [] {
1019                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.FULL_RES),
1020                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW)},
1021                 "In-app RAW -> YUV reprocessing with in-app image analysis",
1022                 /*reprocessType*/ ReprocessType.REMOSAIC),
1023     };
1024 
1025     private static StreamCombinationTemplate sUltraHighResolutionYUVReprocStreamCombinations[] = {
1026         // YUV -> JPEG reprocess + PRIV / YUV preview size stream
1027         new StreamCombinationTemplate(new StreamTemplate [] {
1028                 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.FULL_RES),
1029                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW)},
1030                 "Ultra high resolution YUV -> JPEG reprocessing with separate preview",
1031                 /*reprocessType*/ ReprocessType.YUV),
1032         new StreamCombinationTemplate(new StreamTemplate [] {
1033                 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.FULL_RES),
1034                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW)},
1035                 "Ultra high resolution YUV -> JPEG reprocessing with in-app image analysis",
1036                 /*reprocessType*/ ReprocessType.YUV),
1037 
1038         // YUV -> YUV reprocess + PRIV / YUV preview size stream
1039         new StreamCombinationTemplate(new StreamTemplate [] {
1040                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.FULL_RES),
1041                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW)},
1042                 "Ultra high resolution YUV -> YUV reprocessing with separate preview",
1043                 /*reprocessType*/ ReprocessType.YUV),
1044         new StreamCombinationTemplate(new StreamTemplate [] {
1045                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.FULL_RES),
1046                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW)},
1047                 "Ultra high resolution YUV -> YUV reprocessing with in-app image analysis",
1048                 /*reprocessType*/ ReprocessType.YUV),
1049     };
1050 
1051     private static StreamCombinationTemplate sUltraHighResolutionPRIVReprocStreamCombinations[] = {
1052         new StreamCombinationTemplate(new StreamTemplate [] {
1053                 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.FULL_RES),
1054                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW)},
1055                 "Ultra high resolution PRIVATE -> JPEG reprocessing with separate preview",
1056                 /*reprocessType*/ ReprocessType.PRIVATE),
1057         new StreamCombinationTemplate(new StreamTemplate [] {
1058                 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.FULL_RES),
1059                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW)},
1060                 "Ultra high resolution PRIVATE -> JPEG reprocessing with in-app image analysis",
1061                 /*reprocessType*/ ReprocessType.PRIVATE),
1062     };
1063 
1064     private static StreamCombinationTemplate s10BitOutputStreamCombinations[] = {
1065             new StreamCombinationTemplate(new StreamTemplate [] {
1066                     new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.MAXIMUM)},
1067                     "Simple preview, GPU video processing, or no-preview video recording"),
1068             new StreamCombinationTemplate(new StreamTemplate [] {
1069                     new StreamTemplate(ImageFormat.YCBCR_P010, SizeThreshold.MAXIMUM)},
1070                     "In-application video/image processing"),
1071             new StreamCombinationTemplate(new StreamTemplate [] {
1072                     new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM),
1073                     new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW)},
1074                     "Standard still imaging"),
1075             new StreamCombinationTemplate(new StreamTemplate [] {
1076                     new StreamTemplate(ImageFormat.YCBCR_P010, SizeThreshold.MAXIMUM),
1077                     new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW)},
1078                     "Maximum-resolution in-app processing with preview"),
1079             new StreamCombinationTemplate(new StreamTemplate [] {
1080                     new StreamTemplate(ImageFormat.YCBCR_P010, SizeThreshold.MAXIMUM),
1081                     new StreamTemplate(ImageFormat.YCBCR_P010, SizeThreshold.PREVIEW)},
1082                     "Maximum-resolution two-input in-app processing"),
1083             new StreamCombinationTemplate(new StreamTemplate [] {
1084                     new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.RECORD),
1085                     new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW)},
1086                     "High-resolution video recording with preview"),
1087             new StreamCombinationTemplate(new StreamTemplate [] {
1088                     new StreamTemplate(ImageFormat.YCBCR_P010, SizeThreshold.RECORD),
1089                     new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.RECORD),
1090                     new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW)},
1091                     "High-resolution recording with in-app snapshot"),
1092             new StreamCombinationTemplate(new StreamTemplate [] {
1093                     new StreamTemplate(ImageFormat.JPEG, SizeThreshold.RECORD),
1094                     new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.RECORD),
1095                     new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW)},
1096                     "High-resolution recording with video snapshot"),
1097     };
1098 
1099     private static StreamCombinationTemplate sStreamUseCaseCombinations[] = {
1100         // Single stream combinations
1101         new StreamCombinationTemplate(new StreamTemplate [] {
1102                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW,
1103                         STREAM_USE_CASE_PREVIEW) },
1104                 "Simple preview"),
1105         new StreamCombinationTemplate(new StreamTemplate [] {
1106                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW,
1107                         STREAM_USE_CASE_PREVIEW) },
1108                 "Simple in-application image processing"),
1109         new StreamCombinationTemplate(new StreamTemplate [] {
1110                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.RECORD,
1111                         STREAM_USE_CASE_RECORD) },
1112                 "Simple video recording"),
1113         new StreamCombinationTemplate(new StreamTemplate [] {
1114                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.RECORD,
1115                         STREAM_USE_CASE_RECORD) },
1116                 "Simple in-application video processing"),
1117         new StreamCombinationTemplate(new StreamTemplate [] {
1118                 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM,
1119                         STREAM_USE_CASE_STILL_CAPTURE) },
1120                 "Simple JPEG still capture"),
1121         new StreamCombinationTemplate(new StreamTemplate [] {
1122                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.MAXIMUM,
1123                         STREAM_USE_CASE_STILL_CAPTURE) },
1124                 "Simple YUV still capture"),
1125         new StreamCombinationTemplate(new StreamTemplate [] {
1126                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.s1440p,
1127                         STREAM_USE_CASE_PREVIEW_VIDEO_STILL) },
1128                 "Multi-purpose stream for preview, video and still capture"),
1129         new StreamCombinationTemplate(new StreamTemplate [] {
1130                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.s1440p,
1131                         STREAM_USE_CASE_PREVIEW_VIDEO_STILL) },
1132                 "Multi-purpose YUV stream for preview, video and still capture"),
1133         new StreamCombinationTemplate(new StreamTemplate [] {
1134                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.s1440p,
1135                         STREAM_USE_CASE_VIDEO_CALL) },
1136                 "Simple video call"),
1137         new StreamCombinationTemplate(new StreamTemplate [] {
1138                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.s1440p,
1139                         STREAM_USE_CASE_VIDEO_CALL) },
1140                 "Simple YUV video call"),
1141 
1142         // 2-stream combinations
1143         new StreamCombinationTemplate(new StreamTemplate [] {
1144                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW,
1145                         STREAM_USE_CASE_PREVIEW),
1146                 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM,
1147                         STREAM_USE_CASE_STILL_CAPTURE)},
1148                 "Preview with JPEG still image capture"),
1149         new StreamCombinationTemplate(new StreamTemplate [] {
1150                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW,
1151                         STREAM_USE_CASE_PREVIEW),
1152                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.MAXIMUM,
1153                         STREAM_USE_CASE_STILL_CAPTURE)},
1154                 "Preview with YUV still image capture"),
1155         new StreamCombinationTemplate(new StreamTemplate [] {
1156                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW,
1157                         STREAM_USE_CASE_PREVIEW),
1158                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.RECORD,
1159                         STREAM_USE_CASE_RECORD)},
1160                 "Preview with video recording"),
1161         new StreamCombinationTemplate(new StreamTemplate [] {
1162                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW,
1163                         STREAM_USE_CASE_PREVIEW),
1164                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.RECORD,
1165                         STREAM_USE_CASE_RECORD)},
1166                 "Preview with in-application video processing"),
1167         new StreamCombinationTemplate(new StreamTemplate [] {
1168                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW,
1169                         STREAM_USE_CASE_PREVIEW),
1170                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW,
1171                         STREAM_USE_CASE_PREVIEW)},
1172                 "Preview with in-application image processing"),
1173         new StreamCombinationTemplate(new StreamTemplate [] {
1174                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW,
1175                         STREAM_USE_CASE_PREVIEW),
1176                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.s1440p,
1177                         STREAM_USE_CASE_VIDEO_CALL)},
1178                 "Preview with video call"),
1179         new StreamCombinationTemplate(new StreamTemplate [] {
1180                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW,
1181                         STREAM_USE_CASE_PREVIEW),
1182                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.s1440p,
1183                         STREAM_USE_CASE_VIDEO_CALL)},
1184                 "Preview with YUV video call"),
1185         new StreamCombinationTemplate(new StreamTemplate [] {
1186                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.s1440p,
1187                         STREAM_USE_CASE_PREVIEW_VIDEO_STILL),
1188                 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM,
1189                         STREAM_USE_CASE_STILL_CAPTURE)},
1190                 "Multi-purpose stream with JPEG still capture"),
1191         new StreamCombinationTemplate(new StreamTemplate [] {
1192                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.s1440p,
1193                         STREAM_USE_CASE_PREVIEW_VIDEO_STILL),
1194                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.MAXIMUM,
1195                         STREAM_USE_CASE_STILL_CAPTURE)},
1196                 "Multi-purpose stream with YUV still capture"),
1197         new StreamCombinationTemplate(new StreamTemplate [] {
1198                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.s1440p,
1199                         STREAM_USE_CASE_PREVIEW_VIDEO_STILL),
1200                 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM,
1201                         STREAM_USE_CASE_STILL_CAPTURE)},
1202                 "Multi-purpose YUV stream with JPEG still capture"),
1203         new StreamCombinationTemplate(new StreamTemplate [] {
1204                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.s1440p,
1205                         STREAM_USE_CASE_PREVIEW_VIDEO_STILL),
1206                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.MAXIMUM,
1207                         STREAM_USE_CASE_STILL_CAPTURE)},
1208                 "Multi-purpose YUV stream with YUV still capture"),
1209         new StreamCombinationTemplate(new StreamTemplate [] {
1210                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW,
1211                         STREAM_USE_CASE_STILL_CAPTURE),
1212                 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM,
1213                         STREAM_USE_CASE_STILL_CAPTURE)},
1214                 "YUV and JPEG concurrent still image capture (for testing)"),
1215 
1216         // 3-stream combinations
1217         new StreamCombinationTemplate(new StreamTemplate [] {
1218                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW,
1219                         STREAM_USE_CASE_PREVIEW),
1220                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.RECORD,
1221                         STREAM_USE_CASE_RECORD),
1222                 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.RECORD,
1223                         STREAM_USE_CASE_STILL_CAPTURE)},
1224                 "Preview, video record and JPEG video snapshot"),
1225         new StreamCombinationTemplate(new StreamTemplate [] {
1226                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW,
1227                         STREAM_USE_CASE_PREVIEW),
1228                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.RECORD,
1229                         STREAM_USE_CASE_RECORD),
1230                 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.RECORD,
1231                         STREAM_USE_CASE_STILL_CAPTURE)},
1232                 "Preview, in-application video processing and JPEG video snapshot"),
1233         new StreamCombinationTemplate(new StreamTemplate [] {
1234                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW,
1235                         STREAM_USE_CASE_PREVIEW),
1236                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW,
1237                         STREAM_USE_CASE_PREVIEW),
1238                 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM,
1239                         STREAM_USE_CASE_STILL_CAPTURE)},
1240                 "Preview, in-application image processing, and JPEG still image capture"),
1241     };
1242 
1243     private static StreamCombinationTemplate sCroppedRawStreamUseCaseCombinations[] = {
1244         // Single stream combination
1245         new StreamCombinationTemplate(new StreamTemplate [] {
1246                 new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.MAXIMUM,
1247                         STREAM_USE_CASE_CROPPED_RAW)},
1248                 "Cropped RAW still image capture without preview"),
1249 
1250         // 2 Stream combinations
1251         new StreamCombinationTemplate(new StreamTemplate [] {
1252                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW,
1253                         STREAM_USE_CASE_PREVIEW),
1254                 new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.MAXIMUM,
1255                         STREAM_USE_CASE_CROPPED_RAW)},
1256                 "Cropped RAW still image capture with preview"),
1257         new StreamCombinationTemplate(new StreamTemplate [] {
1258                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW,
1259                         STREAM_USE_CASE_PREVIEW),
1260                 new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.MAXIMUM,
1261                         STREAM_USE_CASE_CROPPED_RAW)},
1262                 "In-app image processing with cropped RAW still image capture"),
1263 
1264         // 3 stream combinations
1265         new StreamCombinationTemplate(new StreamTemplate [] {
1266                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW,
1267                         STREAM_USE_CASE_PREVIEW),
1268                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.MAXIMUM,
1269                         STREAM_USE_CASE_STILL_CAPTURE),
1270                 new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.MAXIMUM,
1271                         STREAM_USE_CASE_CROPPED_RAW)},
1272                 "Preview with YUV and RAW still image capture"),
1273         new StreamCombinationTemplate(new StreamTemplate [] {
1274                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW,
1275                         STREAM_USE_CASE_PREVIEW),
1276                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.MAXIMUM,
1277                         STREAM_USE_CASE_STILL_CAPTURE),
1278                 new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.MAXIMUM,
1279                         STREAM_USE_CASE_CROPPED_RAW)},
1280                 "In-app image processing with YUV and RAW still image capture"),
1281         new StreamCombinationTemplate(new StreamTemplate [] {
1282                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW,
1283                         STREAM_USE_CASE_PREVIEW),
1284                 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM,
1285                         STREAM_USE_CASE_STILL_CAPTURE),
1286                 new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.MAXIMUM,
1287                         STREAM_USE_CASE_CROPPED_RAW)},
1288                 "Preview with JPEG and RAW still image capture"),
1289         new StreamCombinationTemplate(new StreamTemplate [] {
1290                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW,
1291                         STREAM_USE_CASE_PREVIEW),
1292                 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM,
1293                         STREAM_USE_CASE_STILL_CAPTURE),
1294                 new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.MAXIMUM,
1295                         STREAM_USE_CASE_CROPPED_RAW)},
1296                 "In-app image processing with JPEG and RAW still image capture"),
1297         new StreamCombinationTemplate(new StreamTemplate [] {
1298                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW,
1299                         STREAM_USE_CASE_PREVIEW),
1300                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW,
1301                         STREAM_USE_CASE_RECORD),
1302                 new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.MAXIMUM,
1303                         STREAM_USE_CASE_CROPPED_RAW)},
1304                 "Preview with video recording and RAW snapshot"),
1305         new StreamCombinationTemplate(new StreamTemplate [] {
1306                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW,
1307                         STREAM_USE_CASE_PREVIEW),
1308                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW,
1309                         STREAM_USE_CASE_PREVIEW),
1310                 new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.MAXIMUM,
1311                         STREAM_USE_CASE_CROPPED_RAW)},
1312                 "Preview with in-app image processing and RAW still image capture"),
1313         new StreamCombinationTemplate(new StreamTemplate [] {
1314                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW,
1315                         STREAM_USE_CASE_PREVIEW),
1316                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW,
1317                         STREAM_USE_CASE_PREVIEW),
1318                 new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.MAXIMUM,
1319                         STREAM_USE_CASE_CROPPED_RAW)},
1320                 "Two input in-app processing and RAW still image capture"),
1321     };
1322 
1323     private static StreamCombinationTemplate sPreviewStabilizedStreamCombinations[] = {
1324         // 1 stream combinations
1325         new StreamCombinationTemplate(new StreamTemplate [] {
1326                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.s1440p)},
1327                 "Stabilized preview, GPU video processing, or no-preview stabilized recording"),
1328         new StreamCombinationTemplate(new StreamTemplate [] {
1329                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.s1440p)},
1330                 "Stabilized preview, GPU video processing, or no-preview stabilized recording"),
1331         //2 stream combinations
1332         new StreamCombinationTemplate(new StreamTemplate [] {
1333                 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM),
1334                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.s1440p)},
1335                 "Standard JPEG still imaging with stabilized preview"),
1336         new StreamCombinationTemplate(new StreamTemplate [] {
1337                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.MAXIMUM),
1338                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.s1440p)},
1339                 "Standard YUV still imaging with stabilized preview"),
1340         new StreamCombinationTemplate(new StreamTemplate [] {
1341                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.MAXIMUM),
1342                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.s1440p)},
1343                 "Standard YUV still imaging with stabilized in-app image processing stream"),
1344         new StreamCombinationTemplate(new StreamTemplate [] {
1345                 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM),
1346                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.s1440p)},
1347                 "Standard JPEG still imaging with stabilized in-app image processing stream"),
1348 
1349         new StreamCombinationTemplate(new StreamTemplate [] {
1350                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.s1440p),
1351                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW)},
1352                 "High-resolution video recording with preview both streams stabilized"),
1353         new StreamCombinationTemplate(new StreamTemplate [] {
1354                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.s1440p),
1355                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW)},
1356                 "High-resolution video recording with preview both streams stabilized"),
1357         new StreamCombinationTemplate(new StreamTemplate [] {
1358                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.s1440p),
1359                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW)},
1360                 "High-resolution video recording with preview both streams stabilized"),
1361         new StreamCombinationTemplate(new StreamTemplate [] {
1362                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.s1440p),
1363                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW)},
1364                 "High-resolution video recording with preview both streams stabilized"),
1365     };
1366 
1367     /**
1368      * Helper builder class to generate a list of available mandatory stream combinations.
1369      * @hide
1370      */
1371     public static final class Builder {
1372         private Size mDisplaySize;
1373         private List<Integer> mCapabilities;
1374         private int mHwLevel, mCameraId;
1375         private StreamConfigurationMap mStreamConfigMap;
1376         private StreamConfigurationMap mStreamConfigMapMaximumResolution;
1377         private boolean mIsHiddenPhysicalCamera;
1378         private boolean mIsPreviewStabilizationSupported = false;
1379         private boolean mIsCroppedRawSupported = false;
1380 
1381         private final Size kPreviewSizeBound = new Size(1920, 1088);
1382 
1383         /**
1384          * Helper class to be used to generate the available mandatory stream combinations.
1385          *
1386          * @param cameraId Current camera id.
1387          * @param hwLevel The camera HW level as reported by android.info.supportedHardwareLevel.
1388          * @param displaySize The device display size.
1389          * @param capabilities The camera device capabilities.
1390          * @param sm The camera device stream configuration map.
1391          * @param smMaxResolution The camera device stream configuration map when it runs in max
1392          *                        resolution mode.
1393          * @param previewStabilization The camera device supports preview stabilization.
1394          * @param croppedRaw The camera device supports the cropped raw stream use case.
1395          */
Builder(int cameraId, int hwLevel, @NonNull Size displaySize, @NonNull List<Integer> capabilities, @NonNull StreamConfigurationMap sm, StreamConfigurationMap smMaxResolution, boolean previewStabilization, boolean isCroppedRawSupported)1396         public Builder(int cameraId, int hwLevel, @NonNull Size displaySize,
1397                 @NonNull List<Integer> capabilities, @NonNull StreamConfigurationMap sm,
1398                 StreamConfigurationMap smMaxResolution, boolean previewStabilization,
1399                 boolean isCroppedRawSupported) {
1400             mCameraId = cameraId;
1401             mDisplaySize = displaySize;
1402             mCapabilities = capabilities;
1403             mStreamConfigMap = sm;
1404             mStreamConfigMapMaximumResolution = smMaxResolution;
1405             mHwLevel = hwLevel;
1406             mIsHiddenPhysicalCamera =
1407                     CameraManager.isHiddenPhysicalCamera(Integer.toString(mCameraId));
1408             mIsPreviewStabilizationSupported = previewStabilization;
1409             mIsCroppedRawSupported = isCroppedRawSupported;
1410         }
1411 
1412         private @Nullable List<MandatoryStreamCombination>
getAvailableMandatoryStreamCombinationsInternal( StreamCombinationTemplate []chosenStreamCombinations, boolean s10Bit)1413         getAvailableMandatoryStreamCombinationsInternal(
1414                 StreamCombinationTemplate []chosenStreamCombinations, boolean s10Bit) {
1415 
1416             HashMap<Pair<SizeThreshold, Integer>, List<Size>> availableSizes =
1417                     enumerateAvailableSizes();
1418             if (availableSizes == null) {
1419                 Log.e(TAG, "Available size enumeration failed!");
1420                 return null;
1421             }
1422 
1423             ArrayList<MandatoryStreamCombination> availableStreamCombinations = new ArrayList<>();
1424             availableStreamCombinations.ensureCapacity(chosenStreamCombinations.length);
1425             for (StreamCombinationTemplate combTemplate : chosenStreamCombinations) {
1426                 ArrayList<MandatoryStreamInformation> streamsInfo = new ArrayList<>();
1427                 streamsInfo.ensureCapacity(combTemplate.mStreamTemplates.length);
1428                 for (StreamTemplate template : combTemplate.mStreamTemplates) {
1429                     List<Size> sizes = null;
1430                     Pair<SizeThreshold, Integer> pair;
1431                     pair = new Pair<>(template.mSizeThreshold, new Integer(template.mFormat));
1432                     sizes = availableSizes.get(pair);
1433                     if (s10Bit && template.mFormat == ImageFormat.YCBCR_P010) {
1434                         // Make sure that exactly the same 10 and 8-bit YUV streams sizes are
1435                         // supported
1436                         pair = new Pair<>(template.mSizeThreshold,
1437                                 new Integer(ImageFormat.YUV_420_888));
1438                         HashSet<Size> sdrYuvSizes = new HashSet<>(availableSizes.get(pair));
1439                         if (!sdrYuvSizes.equals(new HashSet<>(sizes))) {
1440                             Log.e(TAG, "The supported 10-bit YUV sizes are different from the"
1441                                     + " supported 8-bit YUV sizes!");
1442                             return null;
1443                         }
1444                     }
1445 
1446                     MandatoryStreamInformation streamInfo;
1447                     boolean isMaximumSize =
1448                             (template.mSizeThreshold == SizeThreshold.MAXIMUM);
1449                     try {
1450                         streamInfo = new MandatoryStreamInformation(sizes, template.mFormat,
1451                                 isMaximumSize, /*isInput*/ false,
1452                                 /*isUltraHighResolution*/ false,
1453                                 /*is10BitCapable*/ s10Bit ? template.mFormat != ImageFormat.JPEG :
1454                                         false);
1455                     } catch (IllegalArgumentException e) {
1456                         Log.e(TAG, "No available sizes found for format: " + template.mFormat +
1457                                 " size threshold: " + template.mSizeThreshold + " combination: " +
1458                                 combTemplate.mDescription);
1459                         return null;
1460                     }
1461                     streamsInfo.add(streamInfo);
1462                 }
1463 
1464                 MandatoryStreamCombination streamCombination;
1465                 try {
1466                     streamCombination = new MandatoryStreamCombination(streamsInfo,
1467                             combTemplate.mDescription, /*isReprocessable*/ false);
1468                 } catch (IllegalArgumentException e) {
1469                     Log.e(TAG, "No stream information for mandatory combination: "
1470                             + combTemplate.mDescription);
1471                     return null;
1472                 }
1473 
1474                 availableStreamCombinations.add(streamCombination);
1475             }
1476 
1477             return Collections.unmodifiableList(availableStreamCombinations);
1478         }
1479 
1480         /**
1481          * Retrieve a list of all available mandatory stream combinations for devices supporting
1482          * preview stabilization.
1483          *
1484          * @return a non-modifiable list of supported mandatory stream combinations on which
1485          *         preview stabilization is supported.,
1486          *         null in case device is not 10-bit output capable.
1487          */
1488         public @Nullable List<MandatoryStreamCombination>
getAvailableMandatoryPreviewStabilizedStreamCombinations()1489         getAvailableMandatoryPreviewStabilizedStreamCombinations() {
1490             // Since preview stabilization support is optional, we mandate these stream
1491             // combinations regardless of camera device capabilities.
1492 
1493             StreamCombinationTemplate []chosenStreamCombinations =
1494                     sPreviewStabilizedStreamCombinations;
1495 
1496             if (!mIsPreviewStabilizationSupported) {
1497                 Log.v(TAG, "Device does not support preview stabilization");
1498                  return null;
1499              }
1500 
1501             return getAvailableMandatoryStreamCombinationsInternal(chosenStreamCombinations,
1502                     /*10bit*/false);
1503         }
1504 
1505 
1506         /**
1507          * Retrieve a list of all available mandatory 10-bit output capable stream combinations.
1508          *
1509          * @return a non-modifiable list of supported mandatory 10-bit capable stream combinations,
1510          *         null in case device is not 10-bit output capable.
1511          */
1512         public @Nullable List<MandatoryStreamCombination>
getAvailableMandatory10BitStreamCombinations()1513         getAvailableMandatory10BitStreamCombinations() {
1514             // Since 10-bit streaming support is optional, we mandate these stream
1515             // combinations regardless of camera device capabilities.
1516 
1517             StreamCombinationTemplate []chosenStreamCombinations = s10BitOutputStreamCombinations;
1518             if (!is10BitOutputSupported()) {
1519                 Log.v(TAG, "Device is not able to output 10-bit!");
1520                 return null;
1521             }
1522             return getAvailableMandatoryStreamCombinationsInternal(chosenStreamCombinations,
1523                     /*10bit*/true);
1524         }
1525 
1526         /**
1527           * Retrieve a list of all available mandatory stream combinations with stream use cases.
1528           * when the camera device has {@link
1529           * CameraMetdata.REQUEST_AVAILABLE_CAPABILITIES_STREAM_USE_CASE} capability.
1530           *
1531           * @return a non-modifiable list of supported mandatory stream combinations with stream
1532           *         use cases. Null in case the device doesn't have {@link
1533           *         CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_STREAM_USE_CASE}
1534           *         capability.
1535           */
1536         public @NonNull List<MandatoryStreamCombination>
getAvailableMandatoryStreamUseCaseCombinations()1537                 getAvailableMandatoryStreamUseCaseCombinations() {
1538             if (!isCapabilitySupported(
1539                     CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_STREAM_USE_CASE)) {
1540                 return null;
1541             }
1542 
1543             HashMap<Pair<SizeThreshold, Integer>, List<Size>> availableSizes =
1544                     enumerateAvailableSizes();
1545             if (availableSizes == null) {
1546                 Log.e(TAG, "Available size enumeration failed!");
1547                 return null;
1548             }
1549             ArrayList<StreamCombinationTemplate> availableTemplates =
1550                     new ArrayList<StreamCombinationTemplate> ();
1551             availableTemplates.addAll(Arrays.asList(sStreamUseCaseCombinations));
1552 
1553             ArrayList<MandatoryStreamCombination> availableStreamCombinations = new ArrayList<>();
1554             int capacity = sStreamUseCaseCombinations.length;
1555             if (mIsCroppedRawSupported) {
1556                 capacity += sCroppedRawStreamUseCaseCombinations.length;
1557                 availableStreamCombinations.ensureCapacity(capacity);
1558                 availableTemplates.addAll(Arrays.asList(sCroppedRawStreamUseCaseCombinations));
1559             }
1560              else {
1561                 availableStreamCombinations.ensureCapacity(capacity);
1562              }
1563 
1564 
1565             for (StreamCombinationTemplate combTemplate : availableTemplates) {
1566                 ArrayList<MandatoryStreamInformation> streamsInfo =
1567                         new ArrayList<MandatoryStreamInformation>();
1568                 streamsInfo.ensureCapacity(combTemplate.mStreamTemplates.length);
1569 
1570                 for (StreamTemplate template : combTemplate.mStreamTemplates) {
1571                     List<Size> sizes = null;
1572                     Pair<SizeThreshold, Integer> pair;
1573                     pair = new Pair<SizeThreshold, Integer>(template.mSizeThreshold,
1574                             new Integer(template.mFormat));
1575                     sizes = availableSizes.get(pair);
1576 
1577                     MandatoryStreamInformation streamInfo;
1578                     boolean isMaximumSize =
1579                             (template.mSizeThreshold == SizeThreshold.MAXIMUM);
1580                     try {
1581                         streamInfo = new MandatoryStreamInformation(sizes, template.mFormat,
1582                                 isMaximumSize, /*isInput*/false, /*isUltraHighResolution*/false,
1583                                 /*is10BitCapable*/ false, template.mStreamUseCase);
1584                     } catch (IllegalArgumentException e) {
1585                         Log.e(TAG, "No available sizes found for format: " + template.mFormat +
1586                                 " size threshold: " + template.mSizeThreshold + " combination: " +
1587                                 combTemplate.mDescription);
1588                         return null;
1589                     }
1590                     streamsInfo.add(streamInfo);
1591                 }
1592 
1593                 MandatoryStreamCombination streamCombination;
1594                 try {
1595                     streamCombination = new MandatoryStreamCombination(streamsInfo,
1596                             combTemplate.mDescription, /*isReprocessable*/ false);
1597                 } catch (IllegalArgumentException e) {
1598                     Log.e(TAG, "No stream information for mandatory combination: "
1599                             + combTemplate.mDescription);
1600                     return null;
1601                 }
1602 
1603                 availableStreamCombinations.add(streamCombination);
1604             }
1605 
1606             return Collections.unmodifiableList(availableStreamCombinations);
1607         }
1608 
1609         /**
1610           * Retrieve a list of all available mandatory concurrent stream combinations.
1611           * This method should only be called for devices which are listed in combinations returned
1612           * by CameraManager.getConcurrentCameraIds.
1613           *
1614           * @return a non-modifiable list of supported mandatory concurrent stream combinations.
1615           */
1616         public @NonNull List<MandatoryStreamCombination>
getAvailableMandatoryConcurrentStreamCombinations()1617                 getAvailableMandatoryConcurrentStreamCombinations() {
1618             // Since concurrent streaming support is optional, we mandate these stream
1619             // combinations regardless of camera device capabilities.
1620 
1621             StreamCombinationTemplate []chosenStreamCombinations = sConcurrentStreamCombinations;
1622             if (!isColorOutputSupported()) {
1623                 Log.v(TAG, "Device is not backward compatible, depth streams are mandatory!");
1624                 chosenStreamCombinations = sConcurrentDepthOnlyStreamCombinations;
1625             }
1626             Size sizeVGAp = new Size(640, 480);
1627             Size size720p = new Size(1280, 720);
1628             Size size1440p = new Size(1920, 1440);
1629 
1630             ArrayList<MandatoryStreamCombination> availableConcurrentStreamCombinations =
1631                     new ArrayList<MandatoryStreamCombination>();
1632             availableConcurrentStreamCombinations.ensureCapacity(
1633                     chosenStreamCombinations.length);
1634             for (StreamCombinationTemplate combTemplate : chosenStreamCombinations) {
1635                 ArrayList<MandatoryStreamInformation> streamsInfo =
1636                         new ArrayList<MandatoryStreamInformation>();
1637                 streamsInfo.ensureCapacity(combTemplate.mStreamTemplates.length);
1638                 for (StreamTemplate template : combTemplate.mStreamTemplates) {
1639                     MandatoryStreamInformation streamInfo;
1640                     List<Size> sizes = new ArrayList<Size>();
1641                     Size formatSize = null;
1642                     switch (template.mSizeThreshold) {
1643                         case s1440p:
1644                             formatSize = size1440p;
1645                             break;
1646                         case VGA:
1647                             formatSize = sizeVGAp;
1648                             break;
1649                         default:
1650                             formatSize = size720p;
1651                     }
1652                     Size sizeChosen =
1653                             getMinSize(formatSize,
1654                                     getMaxSize(mStreamConfigMap.getOutputSizes(template.mFormat)));
1655                     sizes.add(sizeChosen);
1656                     try {
1657                         streamInfo = new MandatoryStreamInformation(sizes, template.mFormat,
1658                             /*isMaximumSize*/false);
1659                     } catch (IllegalArgumentException e) {
1660                         String cause = "No available sizes found for format: " + template.mFormat
1661                                 + " size threshold: " + template.mSizeThreshold + " combination: "
1662                                 + combTemplate.mDescription;
1663                         throw new RuntimeException(cause, e);
1664                     }
1665                     streamsInfo.add(streamInfo);
1666                 }
1667 
1668                 MandatoryStreamCombination streamCombination;
1669                 try {
1670                     streamCombination = new MandatoryStreamCombination(streamsInfo,
1671                             combTemplate.mDescription, /*isReprocess*/false);
1672                 } catch (IllegalArgumentException e) {
1673                     String cause =  "No stream information for mandatory combination: "
1674                             + combTemplate.mDescription;
1675                     throw new RuntimeException(cause, e);
1676                 }
1677                 availableConcurrentStreamCombinations.add(streamCombination);
1678             }
1679             return Collections.unmodifiableList(availableConcurrentStreamCombinations);
1680         }
1681 
1682         /**
1683          * Retrieve a list of all available mandatory stream combinations supported when
1684          * {@link CaptureRequest#ANDROID_SENSOR_PIXEL_MODE} is set to
1685          * {@link CameraMetadata#ANDROID_SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION}.
1686          *
1687          * @return a non-modifiable list of supported mandatory stream combinations or
1688          *         null in case device is not backward compatible or the method encounters
1689          *         an error.
1690          */
1691         public @NonNull List<MandatoryStreamCombination>
getAvailableMandatoryMaximumResolutionStreamCombinations()1692                 getAvailableMandatoryMaximumResolutionStreamCombinations() {
1693 
1694             if (!isColorOutputSupported()) {
1695                 Log.v(TAG, "Device is not backward compatible!, no mandatory maximum res streams");
1696                 return null;
1697             }
1698 
1699             ArrayList<StreamCombinationTemplate> chosenStreamCombinationTemplates =
1700                     new ArrayList<StreamCombinationTemplate>();
1701 
1702             chosenStreamCombinationTemplates.addAll(
1703                     Arrays.asList(sUltraHighResolutionStreamCombinations));
1704 
1705             ArrayList<MandatoryStreamCombination> availableStreamCombinations =
1706                     new ArrayList<MandatoryStreamCombination>();
1707             boolean addRemosaicReprocessing = isRemosaicReprocessingSupported();
1708 
1709             int remosaicSize = 0;
1710             Size [] maxResYUVInputSizes =
1711                     mStreamConfigMapMaximumResolution.getInputSizes(ImageFormat.YUV_420_888);
1712             Size [] maxResPRIVInputSizes =
1713                     mStreamConfigMapMaximumResolution.getInputSizes(ImageFormat.PRIVATE);
1714 
1715             if (addRemosaicReprocessing) {
1716                 remosaicSize += sUltraHighResolutionReprocStreamCombinations.length;
1717                 chosenStreamCombinationTemplates.addAll(
1718                         Arrays.asList(sUltraHighResolutionReprocStreamCombinations));
1719             }
1720 
1721             if (maxResYUVInputSizes != null && maxResYUVInputSizes.length != 0) {
1722                 remosaicSize += sUltraHighResolutionYUVReprocStreamCombinations.length;
1723                 chosenStreamCombinationTemplates.addAll(
1724                         Arrays.asList(sUltraHighResolutionYUVReprocStreamCombinations));
1725             }
1726 
1727             if (maxResPRIVInputSizes != null && maxResPRIVInputSizes.length != 0) {
1728                 remosaicSize += sUltraHighResolutionPRIVReprocStreamCombinations.length;
1729                 chosenStreamCombinationTemplates.addAll(
1730                         Arrays.asList(sUltraHighResolutionPRIVReprocStreamCombinations));
1731 
1732             }
1733             availableStreamCombinations.ensureCapacity(
1734                     chosenStreamCombinationTemplates.size() + remosaicSize);
1735             fillUHMandatoryStreamCombinations(availableStreamCombinations,
1736                     chosenStreamCombinationTemplates);
1737 
1738             return Collections.unmodifiableList(availableStreamCombinations);
1739         }
1740 
createUHSensorMandatoryStreamCombination( StreamCombinationTemplate combTemplate, int substitutedFormat)1741         private MandatoryStreamCombination createUHSensorMandatoryStreamCombination(
1742                 StreamCombinationTemplate combTemplate, int substitutedFormat) {
1743             ArrayList<MandatoryStreamInformation> streamsInfo =
1744                     new ArrayList<MandatoryStreamInformation>();
1745             streamsInfo.ensureCapacity(combTemplate.mStreamTemplates.length);
1746             boolean isReprocess = combTemplate.mReprocessType != ReprocessType.NONE;
1747             if (isReprocess) {
1748                 int format = -1;
1749                 ArrayList<Size> inputSize = new ArrayList<Size>();
1750                 if (combTemplate.mReprocessType == ReprocessType.PRIVATE) {
1751                     inputSize.add(
1752                             getMaxSize(mStreamConfigMapMaximumResolution.getInputSizes(
1753                                     ImageFormat.PRIVATE)));
1754                     format = ImageFormat.PRIVATE;
1755                 } else if (combTemplate.mReprocessType == ReprocessType.REMOSAIC) {
1756                     inputSize.add(
1757                             getMaxSize(mStreamConfigMapMaximumResolution.getInputSizes(
1758                                     ImageFormat.RAW_SENSOR)));
1759                     format = ImageFormat.RAW_SENSOR;
1760                 } else {
1761                     inputSize.add(
1762                             getMaxSize(mStreamConfigMapMaximumResolution.getInputSizes(
1763                                     ImageFormat.YUV_420_888)));
1764                     format = ImageFormat.YUV_420_888;
1765                 }
1766                 streamsInfo.add(new MandatoryStreamInformation(inputSize, format,
1767                         /*isMaximumSize*/false, /*isInput*/true,
1768                         /*isUltraHighResolution*/ true));
1769                 streamsInfo.add(new MandatoryStreamInformation(inputSize, format,
1770                         /*isMaximumSize*/false, /*isInput*/ false,
1771                         /*isUltraHighResolution*/true));
1772             }
1773             HashMap<Pair<SizeThreshold, Integer>, List<Size>> availableDefaultNonRawSizes =
1774                     enumerateAvailableSizes();
1775             if (availableDefaultNonRawSizes == null) {
1776                 Log.e(TAG, "Available size enumeration failed");
1777                 return null;
1778             }
1779             Size[] defaultRawSizes =
1780                     mStreamConfigMap.getOutputSizes(ImageFormat.RAW_SENSOR);
1781             ArrayList<Size> availableDefaultRawSizes = new ArrayList<>();
1782             if (defaultRawSizes != null) {
1783                 availableDefaultRawSizes.ensureCapacity(defaultRawSizes.length);
1784                 availableDefaultRawSizes.addAll(Arrays.asList(defaultRawSizes));
1785             }
1786             for (StreamTemplate template : combTemplate.mStreamTemplates) {
1787                 MandatoryStreamInformation streamInfo;
1788                 List<Size> sizes = new ArrayList<Size>();
1789                 int formatChosen = template.mFormat;
1790                 boolean isUltraHighResolution =
1791                         (template.mSizeThreshold == SizeThreshold.FULL_RES);
1792                 StreamConfigurationMap sm =
1793                         isUltraHighResolution ?
1794                                 mStreamConfigMapMaximumResolution : mStreamConfigMap;
1795                 boolean isMaximumSize = (template.mSizeThreshold == SizeThreshold.MAXIMUM);
1796 
1797                 if (substitutedFormat != ImageFormat.UNKNOWN && isMaximumSize) {
1798                     formatChosen = substitutedFormat;
1799                 }
1800 
1801                 if (isUltraHighResolution) {
1802                     Size [] outputSizes = sm.getOutputSizes(formatChosen);
1803                     Size [] highResolutionOutputSizes =
1804                             sm.getHighResolutionOutputSizes(formatChosen);
1805                     Size maxBurstSize = getMaxSizeOrNull(outputSizes);
1806                     Size maxHighResolutionSize = getMaxSizeOrNull(highResolutionOutputSizes);
1807                     Size chosenMaxSize =
1808                             maxBurstSize != null ? maxBurstSize : maxHighResolutionSize;
1809                     if (maxBurstSize != null && maxHighResolutionSize != null) {
1810                         chosenMaxSize = getMaxSize(maxBurstSize, maxHighResolutionSize);
1811                     }
1812                     sizes.add(chosenMaxSize);
1813                 } else {
1814                     if (formatChosen == ImageFormat.RAW_SENSOR) {
1815                         // RAW_SENSOR always has MAXIMUM threshold.
1816                         sizes = availableDefaultRawSizes;
1817                     } else {
1818                         Pair<SizeThreshold, Integer> pair =
1819                             new Pair<SizeThreshold, Integer>(template.mSizeThreshold,
1820                                     new Integer(formatChosen));
1821                         sizes = availableDefaultNonRawSizes.get(pair);
1822                     }
1823                 }
1824 
1825                 try {
1826                     streamInfo = new MandatoryStreamInformation(sizes, formatChosen,
1827                             isMaximumSize, /*isInput*/ false, isUltraHighResolution);
1828                 } catch (IllegalArgumentException e) {
1829                     String cause = "No available sizes found for format: " + template.mFormat
1830                             + " size threshold: " + template.mSizeThreshold + " combination: "
1831                             + combTemplate.mDescription;
1832                     throw new RuntimeException(cause, e);
1833                 }
1834                 streamsInfo.add(streamInfo);
1835             }
1836 
1837             String formatString = null;
1838             switch (substitutedFormat) {
1839                 case ImageFormat.RAW_SENSOR :
1840                     formatString = "RAW_SENSOR";
1841                     break;
1842                 case ImageFormat.JPEG :
1843                     formatString = "JPEG";
1844                     break;
1845                 default:
1846                     formatString = "YUV";
1847             }
1848 
1849             MandatoryStreamCombination streamCombination;
1850             try {
1851                 streamCombination = new MandatoryStreamCombination(streamsInfo,
1852                         combTemplate.mDescription + " " + formatString + " still-capture",
1853                         isReprocess);
1854             } catch (IllegalArgumentException e) {
1855                 String cause =  "No stream information for mandatory combination: "
1856                         + combTemplate.mDescription;
1857                 throw new RuntimeException(cause, e);
1858             }
1859             return streamCombination;
1860         }
1861 
fillUHMandatoryStreamCombinations( ArrayList<MandatoryStreamCombination> availableStreamCombinations, ArrayList<StreamCombinationTemplate> chosenTemplates)1862         private void fillUHMandatoryStreamCombinations(
1863                 ArrayList<MandatoryStreamCombination> availableStreamCombinations,
1864                 ArrayList<StreamCombinationTemplate> chosenTemplates) {
1865 
1866             for (StreamCombinationTemplate combTemplate : chosenTemplates) {
1867                 MandatoryStreamCombination streamCombination =
1868                         createUHSensorMandatoryStreamCombination(combTemplate,
1869                                   ImageFormat.UNKNOWN);
1870                 availableStreamCombinations.add(streamCombination);
1871                 if (combTemplate.mSubstituteYUV) {
1872                      streamCombination =
1873                             createUHSensorMandatoryStreamCombination(combTemplate,
1874                                     ImageFormat.RAW_SENSOR);
1875                     availableStreamCombinations.add(streamCombination);
1876                     streamCombination =
1877                             createUHSensorMandatoryStreamCombination(combTemplate,
1878                                     ImageFormat.JPEG);
1879                     availableStreamCombinations.add(streamCombination);
1880                 }
1881             }
1882         }
1883 
1884         /**
1885          * Retrieve a list of all available mandatory stream combinations.
1886          *
1887          * @return a non-modifiable list of supported mandatory stream combinations or
1888          *         null in case device is not backward compatible or the method encounters
1889          *         an error.
1890          */
1891         public @Nullable List<MandatoryStreamCombination>
getAvailableMandatoryStreamCombinations()1892             getAvailableMandatoryStreamCombinations() {
1893             if (!isColorOutputSupported()) {
1894                 Log.v(TAG, "Device is not backward compatible!");
1895                 return null;
1896             }
1897 
1898             if ((mCameraId < 0) && !isExternalCamera()) {
1899                 Log.i(TAG, "Invalid camera id");
1900                 return null;
1901             }
1902 
1903             ArrayList<StreamCombinationTemplate> availableTemplates =
1904                     new ArrayList<StreamCombinationTemplate> ();
1905             if (isHardwareLevelAtLeastLegacy()) {
1906                 availableTemplates.addAll(Arrays.asList(sLegacyCombinations));
1907             }
1908 
1909             // External devices are identical to limited devices w.r.t. stream combinations.
1910             if (isHardwareLevelAtLeastLimited() || isExternalCamera()) {
1911                 availableTemplates.addAll(Arrays.asList(sLimitedCombinations));
1912 
1913                 if (isPrivateReprocessingSupported()) {
1914                     availableTemplates.addAll(Arrays.asList(sLimitedPrivateReprocCombinations));
1915                 }
1916 
1917                 if (isYUVReprocessingSupported()) {
1918                     availableTemplates.addAll(Arrays.asList(sLimitedYUVReprocCombinations));
1919                 }
1920 
1921             }
1922 
1923             if (isCapabilitySupported(
1924                     CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE)) {
1925                 availableTemplates.addAll(Arrays.asList(sBurstCombinations));
1926             }
1927 
1928             if (isHardwareLevelAtLeastFull()) {
1929                 availableTemplates.addAll(Arrays.asList(sFullCombinations));
1930 
1931                 if (isPrivateReprocessingSupported()) {
1932                     availableTemplates.addAll(Arrays.asList(sFullPrivateReprocCombinations));
1933                 }
1934 
1935                 if (isYUVReprocessingSupported()) {
1936                     availableTemplates.addAll(Arrays.asList(sFullYUVReprocCombinations));
1937                 }
1938 
1939             }
1940 
1941             if (isCapabilitySupported(
1942                     CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW)) {
1943                 availableTemplates.addAll(Arrays.asList(sRawCombinations));
1944 
1945                 if (isPrivateReprocessingSupported()) {
1946                     availableTemplates.addAll(Arrays.asList(sRAWPrivateReprocCombinations));
1947                 }
1948 
1949                 if (isYUVReprocessingSupported()) {
1950                     availableTemplates.addAll(Arrays.asList(sRAWYUVReprocCombinations));
1951                 }
1952 
1953             }
1954 
1955             if (isHardwareLevelAtLeastLevel3()) {
1956                 availableTemplates.addAll(Arrays.asList(sLevel3Combinations));
1957 
1958                 if (isPrivateReprocessingSupported()) {
1959                     availableTemplates.addAll(Arrays.asList(sLevel3PrivateReprocCombinations));
1960                 }
1961 
1962                 if (isYUVReprocessingSupported()) {
1963                     availableTemplates.addAll(Arrays.asList(sLevel3YUVReprocCombinations));
1964                 }
1965 
1966             }
1967 
1968             return generateAvailableCombinations(availableTemplates);
1969         }
1970 
1971         /**
1972          * Helper method to generate the available stream combinations given the
1973          * list of available combination templates.
1974          *
1975          * @param availableTemplates a list of templates supported by the camera device.
1976          * @return a non-modifiable list of supported mandatory stream combinations or
1977          *         null in case of errors.
1978          */
generateAvailableCombinations( @onNull ArrayList<StreamCombinationTemplate> availableTemplates)1979         private @Nullable List<MandatoryStreamCombination> generateAvailableCombinations(
1980                 @NonNull ArrayList<StreamCombinationTemplate> availableTemplates) {
1981             if (availableTemplates.isEmpty()) {
1982                 Log.e(TAG, "No available stream templates!");
1983                 return null;
1984             }
1985 
1986             HashMap<Pair<SizeThreshold, Integer>, List<Size>> availableSizes =
1987                 enumerateAvailableSizes();
1988             if (availableSizes == null) {
1989                 Log.e(TAG, "Available size enumeration failed!");
1990                 return null;
1991             }
1992 
1993             // RAW only uses MAXIMUM size threshold
1994             Size[] rawSizes = mStreamConfigMap.getOutputSizes(ImageFormat.RAW_SENSOR);
1995             ArrayList<Size> availableRawSizes = new ArrayList<Size>();
1996             if (rawSizes != null) {
1997                 availableRawSizes.ensureCapacity(rawSizes.length);
1998                 availableRawSizes.addAll(Arrays.asList(rawSizes));
1999             }
2000 
2001             Size maxPrivateInputSize = new Size(0, 0);
2002             if (isPrivateReprocessingSupported()) {
2003                 maxPrivateInputSize = getMaxSize(mStreamConfigMap.getInputSizes(
2004                             ImageFormat.PRIVATE));
2005             }
2006 
2007             Size maxYUVInputSize = new Size(0, 0);
2008             if (isYUVReprocessingSupported()) {
2009                 maxYUVInputSize = getMaxSize(mStreamConfigMap.getInputSizes(
2010                             ImageFormat.YUV_420_888));
2011             }
2012 
2013             // Generate the available mandatory stream combinations given the supported templates
2014             // and size ranges.
2015             ArrayList<MandatoryStreamCombination> availableStreamCombinations =
2016                     new ArrayList<MandatoryStreamCombination>();
2017             availableStreamCombinations.ensureCapacity(availableTemplates.size());
2018             for (StreamCombinationTemplate combTemplate : availableTemplates) {
2019                 ArrayList<MandatoryStreamInformation> streamsInfo =
2020                         new ArrayList<MandatoryStreamInformation>();
2021                 streamsInfo.ensureCapacity(combTemplate.mStreamTemplates.length);
2022                 boolean isReprocessable = combTemplate.mReprocessType != ReprocessType.NONE;
2023                 if (isReprocessable) {
2024                     // The first and second streams in a reprocessable combination have the
2025                     // same size and format. The first is the input and the second is the output
2026                     // used for generating the subsequent input buffers.
2027                     ArrayList<Size> inputSize = new ArrayList<Size>();
2028                     int format;
2029                     if (combTemplate.mReprocessType == ReprocessType.PRIVATE) {
2030                         inputSize.add(maxPrivateInputSize);
2031                         format = ImageFormat.PRIVATE;
2032                     } else {
2033                         // Default mandatory streams only have PRIVATE / YUV reprocessing.
2034                         inputSize.add(maxYUVInputSize);
2035                         format = ImageFormat.YUV_420_888;
2036                     }
2037                     streamsInfo.add(new MandatoryStreamInformation(inputSize, format,
2038                                 /*isMaximumSize*/true, /*isInput*/true));
2039                     streamsInfo.add(new MandatoryStreamInformation(inputSize, format,
2040                             /*isMaximumSize*/true));
2041                 }
2042 
2043                 for (StreamTemplate template : combTemplate.mStreamTemplates) {
2044                     List<Size> sizes = null;
2045                     if (template.mFormat == ImageFormat.RAW_SENSOR) {
2046                         sizes = availableRawSizes;
2047                     } else {
2048                         Pair<SizeThreshold, Integer> pair;
2049                         pair = new Pair<SizeThreshold, Integer>(template.mSizeThreshold,
2050                                 new Integer(template.mFormat));
2051                         sizes = availableSizes.get(pair);
2052                     }
2053 
2054                     MandatoryStreamInformation streamInfo;
2055                     boolean isMaximumSize =
2056                             (template.mSizeThreshold == SizeThreshold.MAXIMUM);
2057                     try {
2058                         streamInfo = new MandatoryStreamInformation(sizes, template.mFormat,
2059                                 isMaximumSize);
2060                     } catch (IllegalArgumentException e) {
2061                         Log.e(TAG, "No available sizes found for format: " + template.mFormat +
2062                                 " size threshold: " + template.mSizeThreshold + " combination: " +
2063                                 combTemplate.mDescription);
2064                         return null;
2065                     }
2066                     streamsInfo.add(streamInfo);
2067                 }
2068 
2069                 MandatoryStreamCombination streamCombination;
2070                 try {
2071                     streamCombination = new MandatoryStreamCombination(streamsInfo,
2072                             combTemplate.mDescription, isReprocessable);
2073                 } catch (IllegalArgumentException e) {
2074                     Log.e(TAG, "No stream information for mandatory combination: "
2075                             + combTemplate.mDescription);
2076                     return null;
2077                 }
2078 
2079                 availableStreamCombinations.add(streamCombination);
2080             }
2081 
2082             return Collections.unmodifiableList(availableStreamCombinations);
2083         }
2084 
2085         /**
2086          * Helper method to enumerate all available sizes according to size threshold and format.
2087          */
2088         private @Nullable HashMap<Pair<SizeThreshold, Integer>, List<Size>>
enumerateAvailableSizes()2089             enumerateAvailableSizes() {
2090             final int[] formats = {
2091                 ImageFormat.RAW_SENSOR,
2092                 ImageFormat.PRIVATE,
2093                 ImageFormat.YUV_420_888,
2094                 ImageFormat.JPEG,
2095                 ImageFormat.YCBCR_P010
2096             };
2097             Size recordingMaxSize = new Size(0, 0);
2098             Size previewMaxSize = new Size(0, 0);
2099             Size vgaSize = new Size(640, 480);
2100             Size s720pSize = new Size(1280, 720);
2101             Size s1440pSize = new Size(1920, 1440);
2102             // For external camera, or hidden physical camera, CamcorderProfile may not be
2103             // available, so get maximum recording size using stream configuration map.
2104             if (isExternalCamera() || mIsHiddenPhysicalCamera) {
2105                 recordingMaxSize = getMaxCameraRecordingSize();
2106             } else {
2107                 recordingMaxSize = getMaxRecordingSize();
2108             }
2109             if (recordingMaxSize == null) {
2110                 Log.e(TAG, "Failed to find maximum recording size!");
2111                 return null;
2112             }
2113 
2114             HashMap<Integer, Size[]> allSizes = new HashMap<Integer, Size[]>();
2115             for (int format : formats) {
2116                 Integer intFormat = new Integer(format);
2117                 Size[] sizes = mStreamConfigMap.getOutputSizes(format);
2118                 if (sizes == null) {
2119                     sizes = new Size[0];
2120                 }
2121                 allSizes.put(intFormat, sizes);
2122             }
2123 
2124             List<Size> previewSizes = getSizesWithinBound(
2125                     allSizes.get(new Integer(ImageFormat.PRIVATE)), kPreviewSizeBound);
2126             if ((previewSizes == null) || (previewSizes.isEmpty())) {
2127                 Log.e(TAG, "No preview sizes within preview size bound!");
2128                 return null;
2129             }
2130             List<Size> orderedPreviewSizes = getAscendingOrderSizes(previewSizes,
2131                     /*ascending*/false);
2132             previewMaxSize = getMaxPreviewSize(orderedPreviewSizes);
2133 
2134             HashMap<Pair<SizeThreshold, Integer>, List<Size>> availableSizes =
2135                     new HashMap<Pair<SizeThreshold, Integer>, List<Size>>();
2136 
2137             for (int format : formats) {
2138                 Integer intFormat = new Integer(format);
2139                 Size[] sizes = allSizes.get(intFormat);
2140                 Pair<SizeThreshold, Integer> pair = new Pair<SizeThreshold, Integer>(
2141                         SizeThreshold.VGA, intFormat);
2142                 availableSizes.put(pair, getSizesWithinBound(sizes, vgaSize));
2143 
2144                 pair = new Pair<SizeThreshold, Integer>(SizeThreshold.PREVIEW, intFormat);
2145                 availableSizes.put(pair, getSizesWithinBound(sizes, previewMaxSize));
2146 
2147                 pair = new Pair<SizeThreshold, Integer>(SizeThreshold.RECORD, intFormat);
2148                 availableSizes.put(pair, getSizesWithinBound(sizes, recordingMaxSize));
2149 
2150                 pair = new Pair<SizeThreshold, Integer>(SizeThreshold.MAXIMUM, intFormat);
2151                 availableSizes.put(pair, Arrays.asList(sizes));
2152 
2153                 pair = new Pair<SizeThreshold, Integer>(SizeThreshold.s720p, intFormat);
2154                 availableSizes.put(pair, getSizesWithinBound(sizes, s720pSize));
2155 
2156                 pair = new Pair<SizeThreshold, Integer>(SizeThreshold.s1440p, intFormat);
2157                 availableSizes.put(pair, getSizesWithinBound(sizes, s1440pSize));
2158             }
2159 
2160             return availableSizes;
2161         }
2162 
2163         /**
2164          * Compile a list of sizes smaller than or equal to given bound.
2165          * Return an empty list if there is no size smaller than or equal to the bound.
2166          */
getSizesWithinBound(@onNull Size[] sizes, @NonNull Size bound)2167         private static @Nullable List<Size> getSizesWithinBound(@NonNull Size[] sizes,
2168                 @NonNull Size bound) {
2169             ArrayList<Size> ret = new ArrayList<Size>();
2170             for (Size size : sizes) {
2171                 if (size.getWidth() <= bound.getWidth() && size.getHeight() <= bound.getHeight()) {
2172                     ret.add(size);
2173                 }
2174             }
2175 
2176             return ret;
2177         }
2178 
2179         /**
2180          * Return the lower size
2181          */
getMinSize(Size a, Size b)2182         public static @Nullable Size getMinSize(Size a, Size b) {
2183             if (a == null || b == null) {
2184                 throw new IllegalArgumentException("sizes was empty");
2185             }
2186             if (a.getWidth() * a.getHeight() < b.getHeight() * b.getWidth()) {
2187                 return a;
2188             }
2189             return b;
2190         }
2191         /**
2192          * Get the largest size by area.
2193          *
2194          * @param sizes an array of sizes, must have at least 1 element
2195          *
2196          * @return Largest Size
2197          *
2198          * @throws IllegalArgumentException if sizes was null or had 0 elements
2199          */
getMaxSize(@onNull Size... sizes)2200         public static @Nullable Size getMaxSize(@NonNull Size... sizes) {
2201             if (sizes == null || sizes.length == 0) {
2202                 throw new IllegalArgumentException("sizes was empty");
2203             }
2204 
2205             Size sz = sizes[0];
2206             for (Size size : sizes) {
2207                 if (size.getWidth() * size.getHeight() > sz.getWidth() * sz.getHeight()) {
2208                     sz = size;
2209                 }
2210             }
2211 
2212             return sz;
2213         }
2214 
2215         /**
2216          * Get the largest size by area.
2217          *
2218          * @param sizes an array of sizes
2219          *
2220          * @return Largest Size or null if sizes was null or had 0 elements
2221          */
getMaxSizeOrNull(Size... sizes)2222         public static @Nullable Size getMaxSizeOrNull(Size... sizes) {
2223             if (sizes == null || sizes.length == 0) {
2224                 return null;
2225             }
2226 
2227             return getMaxSize(sizes);
2228         }
2229 
2230         /**
2231          * Whether or not the hardware level reported by android.info.supportedHardwareLevel is
2232          * at least the desired one (but could be higher)
2233          */
isHardwareLevelAtLeast(int level)2234         private boolean isHardwareLevelAtLeast(int level) {
2235             final int[] sortedHwLevels = {
2236                 CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY,
2237                 CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_EXTERNAL,
2238                 CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
2239                 CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL,
2240                 CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_3
2241             };
2242             if (level == mHwLevel) {
2243                 return true;
2244             }
2245 
2246             for (int sortedlevel : sortedHwLevels) {
2247                 if (sortedlevel == level) {
2248                     return true;
2249                 } else if (sortedlevel == mHwLevel) {
2250                     return false;
2251                 }
2252             }
2253 
2254             return false;
2255         }
2256 
2257         /**
2258          * Whether or not the camera is an external camera.
2259          *
2260          * @return {@code true} if the device is external, {@code false} otherwise.
2261          */
isExternalCamera()2262         private boolean isExternalCamera() {
2263             return mHwLevel == CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_EXTERNAL;
2264         }
2265 
2266         /**
2267          * Whether or not the hardware level is at least legacy.
2268          *
2269          * @return {@code true} if the device is {@code LEGACY}, {@code false} otherwise.
2270          */
isHardwareLevelAtLeastLegacy()2271         private boolean isHardwareLevelAtLeastLegacy() {
2272             return isHardwareLevelAtLeast(CameraMetadata.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY);
2273         }
2274 
2275         /**
2276          * Whether or not the hardware level is at least limited.
2277          *
2278          * @return {@code true} if the device is {@code LIMITED} or {@code FULL},
2279          *         {@code false} otherwise (i.e. LEGACY).
2280          */
isHardwareLevelAtLeastLimited()2281         private boolean isHardwareLevelAtLeastLimited() {
2282             return isHardwareLevelAtLeast(CameraMetadata.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED);
2283         }
2284 
2285         /**
2286          * Whether or not the hardware level is at least full.
2287          *
2288          * @return {@code true} if the device is {@code FULL}, {@code false} otherwise.
2289          */
isHardwareLevelAtLeastFull()2290         private boolean isHardwareLevelAtLeastFull() {
2291             return isHardwareLevelAtLeast(CameraMetadata.INFO_SUPPORTED_HARDWARE_LEVEL_FULL);
2292         }
2293 
2294         /**
2295          * Whether or not the hardware level is at least Level 3.
2296          *
2297          * @return {@code true} if the device is {@code LEVEL3}, {@code false} otherwise.
2298          */
isHardwareLevelAtLeastLevel3()2299         private boolean isHardwareLevelAtLeastLevel3() {
2300             return isHardwareLevelAtLeast(CameraMetadata.INFO_SUPPORTED_HARDWARE_LEVEL_3);
2301         }
2302 
2303         /**
2304          * Determine whether the current device supports a capability or not.
2305          *
2306          * @param capability (non-negative)
2307          *
2308          * @return {@code true} if the capability is supported, {@code false} otherwise.
2309          *
2310          */
isCapabilitySupported(int capability)2311         private boolean isCapabilitySupported(int capability) {
2312             return mCapabilities.contains(capability);
2313         }
2314 
2315         /**
2316          * Check whether the current device is backward compatible.
2317          */
isColorOutputSupported()2318         private boolean isColorOutputSupported() {
2319             return isCapabilitySupported(
2320                     CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE);
2321         }
2322 
2323         /**
2324          * Check whether the current device supports 10-bit output.
2325          */
is10BitOutputSupported()2326         private boolean is10BitOutputSupported() {
2327             return isCapabilitySupported(
2328                     CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_DYNAMIC_RANGE_TEN_BIT);
2329         }
2330 
2331         /**
2332          * Check whether the current device supports private reprocessing.
2333          */
isPrivateReprocessingSupported()2334         private boolean isPrivateReprocessingSupported() {
2335             return isCapabilitySupported(
2336                     CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_PRIVATE_REPROCESSING);
2337         }
2338 
2339         /**
2340          * Check whether the current device supports YUV reprocessing.
2341          */
isYUVReprocessingSupported()2342         private boolean isYUVReprocessingSupported() {
2343             return isCapabilitySupported(
2344                     CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_YUV_REPROCESSING);
2345         }
2346 
2347         /**
2348          * Check whether the current device supports YUV reprocessing.
2349          */
isRemosaicReprocessingSupported()2350         private boolean isRemosaicReprocessingSupported() {
2351             return isCapabilitySupported(
2352                     CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_REMOSAIC_REPROCESSING);
2353         }
2354 
2355         /**
2356          * Return the maximum supported video size using the camcorder profile information.
2357          *
2358          * @return Maximum supported video size.
2359          */
getMaxRecordingSize()2360         private @Nullable Size getMaxRecordingSize() {
2361             int quality =
2362                     CamcorderProfile.hasProfile(mCameraId, CamcorderProfile.QUALITY_2160P) ?
2363                         CamcorderProfile.QUALITY_2160P :
2364                     CamcorderProfile.hasProfile(mCameraId, CamcorderProfile.QUALITY_1080P) ?
2365                         CamcorderProfile.QUALITY_1080P :
2366                     CamcorderProfile.hasProfile(mCameraId, CamcorderProfile.QUALITY_720P) ?
2367                         CamcorderProfile.QUALITY_720P :
2368                     CamcorderProfile.hasProfile(mCameraId, CamcorderProfile.QUALITY_480P) ?
2369                         CamcorderProfile.QUALITY_480P :
2370                     CamcorderProfile.hasProfile(mCameraId, CamcorderProfile.QUALITY_QVGA) ?
2371                         CamcorderProfile.QUALITY_QVGA :
2372                     CamcorderProfile.hasProfile(mCameraId, CamcorderProfile.QUALITY_CIF) ?
2373                         CamcorderProfile.QUALITY_CIF :
2374                     CamcorderProfile.hasProfile(mCameraId, CamcorderProfile.QUALITY_QCIF) ?
2375                         CamcorderProfile.QUALITY_QCIF :
2376                         -1;
2377 
2378             if (quality < 0) {
2379                 return null;
2380             }
2381 
2382             CamcorderProfile maxProfile = CamcorderProfile.get(mCameraId, quality);
2383             return new Size(maxProfile.videoFrameWidth, maxProfile.videoFrameHeight);
2384         }
2385 
2386         /**
2387          * Return the maximum supported video size for cameras using data from
2388          * the stream configuration map.
2389          *
2390          * @return Maximum supported video size.
2391          */
getMaxCameraRecordingSize()2392         private @NonNull Size getMaxCameraRecordingSize() {
2393             final Size FULLHD = new Size(1920, 1080);
2394 
2395             Size[] videoSizeArr = mStreamConfigMap.getOutputSizes(
2396                     android.media.MediaRecorder.class);
2397             List<Size> sizes = new ArrayList<Size>();
2398             for (Size sz: videoSizeArr) {
2399                 if (sz.getWidth() <= FULLHD.getWidth() && sz.getHeight() <= FULLHD.getHeight()) {
2400                     sizes.add(sz);
2401                 }
2402             }
2403             List<Size> videoSizes = getAscendingOrderSizes(sizes, /*ascending*/false);
2404             for (Size sz : videoSizes) {
2405                 long minFrameDuration = mStreamConfigMap.getOutputMinFrameDuration(
2406                         android.media.MediaRecorder.class, sz);
2407                 // Give some margin for rounding error
2408                 if (minFrameDuration < (1e9 / 29.9)) {
2409                     Log.i(TAG, "External camera " + mCameraId + " has max video size:" + sz);
2410                     return sz;
2411                 }
2412             }
2413             Log.w(TAG, "Camera " + mCameraId + " does not support any 30fps video output");
2414             return FULLHD; // doesn't matter what size is returned here
2415         }
2416 
getMaxPreviewSize(List<Size> orderedPreviewSizes)2417         private @NonNull Size getMaxPreviewSize(List<Size> orderedPreviewSizes) {
2418             if (orderedPreviewSizes != null) {
2419                 for (Size size : orderedPreviewSizes) {
2420                     if ((mDisplaySize.getWidth() >= size.getWidth()) &&
2421                             (mDisplaySize.getHeight() >= size.getHeight())) {
2422                         return size;
2423                     }
2424                 }
2425             }
2426 
2427             Log.w(TAG,"Camera " + mCameraId + " maximum preview size search failed with "
2428                     + "display size " + mDisplaySize);
2429             return kPreviewSizeBound;
2430         }
2431 
2432         /**
2433          * Size comparator that compares the number of pixels it covers.
2434          *
2435          * <p>If two the areas of two sizes are same, compare the widths.</p>
2436          */
2437         public static class SizeComparator implements Comparator<Size> {
2438             @Override
compare(@onNull Size lhs, @NonNull Size rhs)2439             public int compare(@NonNull Size lhs, @NonNull Size rhs) {
2440                 return StreamConfigurationMap.compareSizes(lhs.getWidth(), lhs.getHeight(),
2441                         rhs.getWidth(), rhs.getHeight());
2442             }
2443         }
2444 
2445         /**
2446          * Get a sorted list of sizes from a given size list.
2447          *
2448          * <p>
2449          * The size is compare by area it covers, if the areas are same, then
2450          * compare the widths.
2451          * </p>
2452          *
2453          * @param sizeList The input size list to be sorted
2454          * @param ascending True if the order is ascending, otherwise descending order
2455          * @return The ordered list of sizes
2456          */
getAscendingOrderSizes( @onNull final List<Size> sizeList, boolean ascending)2457         private static @NonNull List<Size> getAscendingOrderSizes(
2458                 @NonNull final List<Size> sizeList, boolean ascending) {
2459             Comparator<Size> comparator = new SizeComparator();
2460             List<Size> sortedSizes = new ArrayList<Size>();
2461             sortedSizes.addAll(sizeList);
2462             Collections.sort(sortedSizes, comparator);
2463             if (!ascending) {
2464                 Collections.reverse(sortedSizes);
2465             }
2466 
2467             return sortedSizes;
2468         }
2469     }
2470 }
2471