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 static com.android.internal.util.Preconditions.*;
22 
23 import android.annotation.NonNull;
24 import android.annotation.Nullable;
25 import android.graphics.ImageFormat;
26 import android.graphics.ImageFormat.Format;
27 import android.hardware.camera2.CameraCharacteristics;
28 import android.hardware.camera2.CameraDevice;
29 import android.hardware.camera2.CameraManager;
30 import android.hardware.camera2.CameraMetadata;
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.List;
44 
45 /**
46  * Immutable class to store the available mandatory stream combination.
47  *
48  * <p>A mandatory stream combination refers to a specific entry in the documented sets of
49  * required stream {@link CameraDevice#createCaptureSession combinations}.
50  * These combinations of streams are required to be supported by the camera device.
51  *
52  * <p>The list of stream combinations is available by invoking
53  * {@link CameraCharacteristics#get} and passing key
54  * {@link android.hardware.camera2.CameraCharacteristics#SCALER_MANDATORY_STREAM_COMBINATIONS}.</p>
55  */
56 public final class MandatoryStreamCombination {
57     private static final String TAG = "MandatoryStreamCombination";
58     /**
59      * Immutable class to store available mandatory stream information.
60      */
61     public static final class MandatoryStreamInformation {
62         private final int mFormat;
63         private final ArrayList<Size> mAvailableSizes = new ArrayList<Size> ();
64         private final boolean mIsInput;
65         private final boolean mIsUltraHighResolution;
66         private final boolean mIsMaximumSize;
67 
68         /**
69          * Create a new {@link MandatoryStreamInformation}.
70          *
71          * @param availableSizes List of possible stream sizes.
72          * @param format Image format.
73          * @param isMaximumSize Whether this is a maximum size stream.
74          *
75          * @throws IllegalArgumentException
76          *              if sizes is empty or if the format was not user-defined in
77          *              ImageFormat/PixelFormat.
78          * @hide
79          */
MandatoryStreamInformation(@onNull List<Size> availableSizes, @Format int format, boolean isMaximumSize)80         public MandatoryStreamInformation(@NonNull List<Size> availableSizes, @Format int format,
81                 boolean isMaximumSize) {
82             this(availableSizes, format, isMaximumSize, /*isInput*/false,
83                     /*isUltraHighResolution*/false);
84         }
85 
86         /**
87          * Create a new {@link MandatoryStreamInformation}.
88          *
89          * @param availableSizes List of possible stream sizes.
90          * @param format Image format.
91          * @param isMaximumSize Whether this is a maximum size stream.
92          * @param isInput Flag indicating whether this stream is input.
93          *
94          * @throws IllegalArgumentException
95          *              if sizes is empty or if the format was not user-defined in
96          *              ImageFormat/PixelFormat.
97          * @hide
98          */
MandatoryStreamInformation(@onNull List<Size> availableSizes, @Format int format, boolean isMaximumSize, boolean isInput)99         public MandatoryStreamInformation(@NonNull List<Size> availableSizes, @Format int format,
100                 boolean isMaximumSize, boolean isInput) {
101             this(availableSizes, format, isMaximumSize, isInput,
102                     /*isUltraHighResolution*/ false);
103         }
104 
105         /**
106          * Create a new {@link MandatoryStreamInformation}.
107          *
108          * @param availableSizes List of possible stream sizes.
109          * @param format Image format.
110          * @param isMaximumSize Whether this is a maximum size stream.
111          * @param isInput Flag indicating whether this stream is input.
112          * @param isUltraHighResolution Flag indicating whether this is a ultra-high resolution
113          *                              stream.
114          *
115          * @throws IllegalArgumentException
116          *              if sizes is empty or if the format was not user-defined in
117          *              ImageFormat/PixelFormat.
118          * @hide
119          */
MandatoryStreamInformation(@onNull List<Size> availableSizes, @Format int format, boolean isMaximumSize, boolean isInput, boolean isUltraHighResolution)120         public MandatoryStreamInformation(@NonNull List<Size> availableSizes, @Format int format,
121                 boolean isMaximumSize, boolean isInput, boolean isUltraHighResolution) {
122             if (availableSizes.isEmpty()) {
123                 throw new IllegalArgumentException("No available sizes");
124             }
125             mAvailableSizes.addAll(availableSizes);
126             mFormat = checkArgumentFormat(format);
127             mIsMaximumSize = isMaximumSize;
128             mIsInput = isInput;
129             mIsUltraHighResolution = isUltraHighResolution;
130         }
131 
132         /**
133          * Confirms whether or not this is an input stream.
134          * @return true in case the stream is input, false otherwise.
135          */
isInput()136         public boolean isInput() {
137             return mIsInput;
138         }
139 
140         /**
141          * Confirms whether or not this is an ultra high resolution stream.
142          *
143          * <p>An 'ultra high resolution' stream is one which has a configuration which appears in
144          * {@link android.hardware.camera2.CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP_MAXIMUM_RESOLUTION},
145          * Streams which are ultra high resolution must not be included with streams which are not
146          * ultra high resolution in the same {@link android.hardware.camera2.CaptureRequest}.</p>
147          *
148          * @return true in case the stream is ultra high resolution, false otherwise.
149         */
isUltraHighResolution()150         public boolean isUltraHighResolution() {
151             return mIsUltraHighResolution;
152         }
153 
154         /**
155          * Confirms whether or not this is a maximum size stream.
156          *
157          * <p>A stream with maximum size is one with the camera device's maximum resolution
158          * for the stream's format as appears in {@link
159          * android.hardware.camera2.CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP}. This
160          * maximum size has the same meaning as the 'MAXIMUM' target size documented in the camera
161          * capture session {@link CameraDevice#createCaptureSession guideline}.</p>
162          *
163          * <p>The application can use a
164          * {@link android.hardware.camera2.MultiResolutionImageReader} for a maximum size
165          * output stream if the camera device supports multi-resolution outputs for the stream's
166          * format. See {@link
167          * android.hardware.camera2.CameraCharacteristics#SCALER_MULTI_RESOLUTION_STREAM_CONFIGURATION_MAP}
168          * for details.</p>
169          *
170          * <p>This is different from the ultra high resolution flag, which applies only to
171          * ultra high resolution sensor camera devices and refers to a stream in
172          * {@link
173          * android.hardware.camera2.CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP_MAXIMUM_RESOLUTION}
174          * instead.</p>
175          *
176          * @return true if the stream is a maximum size stream.
177          */
isMaximumSize()178         public boolean isMaximumSize() {
179             return mIsMaximumSize;
180         }
181 
182         /**
183          * Return the list of available sizes for this mandatory stream.
184          *
185          * <p>Per documented {@link CameraDevice#createCaptureSession guideline} the largest
186          * resolution in the result will be tested and guaranteed to work. If clients want to use
187          * smaller sizes, then the resulting
188          * {@link android.hardware.camera2.params.SessionConfiguration session configuration} can
189          * be tested either by calling {@link CameraDevice#createCaptureSession} or
190          * {@link CameraDevice#isSessionConfigurationSupported}.
191          *
192          * @return non-modifiable ascending list of available sizes.
193          */
getAvailableSizes()194         public @NonNull List<Size> getAvailableSizes() {
195             return Collections.unmodifiableList(mAvailableSizes);
196         }
197 
198         /**
199          * Retrieve the mandatory stream {@code format}.
200          *
201          * @return integer format.
202          */
getFormat()203         public @Format int getFormat() {
204             return mFormat;
205         }
206 
207         /**
208          * Check if this {@link MandatoryStreamInformation} is equal to another
209          * {@link MandatoryStreamInformation}.
210          *
211          * <p>Two vectors are only equal if and only if each of the respective elements is
212          * equal.</p>
213          *
214          * @return {@code true} if the objects were equal, {@code false} otherwise
215          */
216         @Override
equals(final Object obj)217         public boolean equals(final Object obj) {
218             if (obj == null) {
219                 return false;
220             }
221             if (this == obj) {
222                 return true;
223             }
224             if (obj instanceof MandatoryStreamInformation) {
225                 final MandatoryStreamInformation other = (MandatoryStreamInformation) obj;
226                 if ((mFormat != other.mFormat) || (mIsInput != other.mIsInput) ||
227                         (mIsUltraHighResolution != other.mIsUltraHighResolution) ||
228                         (mAvailableSizes.size() != other.mAvailableSizes.size())) {
229                     return false;
230                 }
231 
232                 return mAvailableSizes.equals(other.mAvailableSizes);
233             }
234 
235             return false;
236         }
237 
238         /**
239          * {@inheritDoc}
240          */
241         @Override
hashCode()242         public int hashCode() {
243             return HashCodeHelpers.hashCode(mFormat, Boolean.hashCode(mIsInput),
244                     Boolean.hashCode(mIsUltraHighResolution), mAvailableSizes.hashCode());
245         }
246     }
247 
248     private final String mDescription;
249     private final boolean mIsReprocessable;
250     private final ArrayList<MandatoryStreamInformation> mStreamsInformation =
251             new ArrayList<MandatoryStreamInformation>();
252     /**
253      * Create a new {@link MandatoryStreamCombination}.
254      *
255      * @param streamsInformation list of available streams in the stream combination.
256      * @param description Summary of the stream combination use case.
257      * @param isReprocessable Flag whether the mandatory stream combination is reprocessable.
258      *
259      * @throws IllegalArgumentException
260      *              if stream information is empty
261      * @hide
262      */
MandatoryStreamCombination(@onNull List<MandatoryStreamInformation> streamsInformation, @NonNull String description, boolean isReprocessable)263     public MandatoryStreamCombination(@NonNull List<MandatoryStreamInformation> streamsInformation,
264             @NonNull String description, boolean isReprocessable) {
265         if (streamsInformation.isEmpty()) {
266             throw new IllegalArgumentException("Empty stream information");
267         }
268         mStreamsInformation.addAll(streamsInformation);
269         mDescription = description;
270         mIsReprocessable = isReprocessable;
271     }
272     /**
273      * Get the mandatory stream combination description.
274      *
275      * @return CharSequence with the mandatory combination description.
276      */
getDescription()277     public @NonNull CharSequence getDescription() {
278         return mDescription;
279     }
280 
281     /**
282      * Indicates whether the mandatory stream combination is reprocessable. Reprocessable is defined
283      * as a stream combination that contains one input stream
284      * ({@link MandatoryStreamInformation#isInput} return true).
285      *
286      * @return {@code true} in case the mandatory stream combination contains an input,
287      *         {@code false} otherwise.
288      */
isReprocessable()289     public boolean isReprocessable() {
290         return mIsReprocessable;
291     }
292 
293     /**
294      * Get information about each stream in the mandatory combination.
295      *
296      * @return Non-modifiable list of stream information.
297      *
298      */
getStreamsInformation()299     public @NonNull List<MandatoryStreamInformation> getStreamsInformation() {
300         return Collections.unmodifiableList(mStreamsInformation);
301     }
302 
303     /**
304      * Check if this {@link MandatoryStreamCombination} is equal to another
305      * {@link MandatoryStreamCombination}.
306      *
307      * <p>Two vectors are only equal if and only if each of the respective elements is equal.</p>
308      *
309      * @return {@code true} if the objects were equal, {@code false} otherwise
310      */
311     @Override
equals(final Object obj)312     public boolean equals(final Object obj) {
313         if (obj == null) {
314             return false;
315         }
316         if (this == obj) {
317             return true;
318         }
319         if (obj instanceof MandatoryStreamCombination) {
320             final MandatoryStreamCombination other = (MandatoryStreamCombination) obj;
321             if ((mDescription != other.mDescription) ||
322                     (mIsReprocessable != other.mIsReprocessable) ||
323                     (mStreamsInformation.size() != other.mStreamsInformation.size())) {
324                 return false;
325             }
326 
327             return mStreamsInformation.equals(other.mStreamsInformation);
328         }
329 
330         return false;
331     }
332 
333     /**
334      * {@inheritDoc}
335      */
336     @Override
hashCode()337     public int hashCode() {
338         return HashCodeHelpers.hashCode(Boolean.hashCode(mIsReprocessable), mDescription.hashCode(),
339                 mStreamsInformation.hashCode());
340     }
341 
342     private static enum SizeThreshold { VGA, PREVIEW, RECORD, MAXIMUM, s720p, s1440p, FULL_RES }
343     private static enum ReprocessType { NONE, PRIVATE, YUV, REMOSAIC }
344     private static final class StreamTemplate {
345         public int mFormat;
346         public SizeThreshold mSizeThreshold;
347         public boolean mIsInput;
StreamTemplate(int format, SizeThreshold sizeThreshold)348         public StreamTemplate(int format, SizeThreshold sizeThreshold) {
349             this(format, sizeThreshold, /*isInput*/false);
350         }
StreamTemplate(@ormat int format, @NonNull SizeThreshold sizeThreshold, boolean isInput)351         public StreamTemplate(@Format int format, @NonNull SizeThreshold sizeThreshold,
352                 boolean isInput) {
353             mFormat = format;
354             mSizeThreshold = sizeThreshold;
355             mIsInput = isInput;
356         }
357     }
358 
359     private static final class StreamCombinationTemplate {
360         public StreamTemplate[] mStreamTemplates;
361         public String mDescription;
362         public ReprocessType mReprocessType;
363         // Substitute MAXIMUM size YUV output stream with JPEG / RAW_SENSOR.
364         public boolean mSubstituteYUV = false;
365 
StreamCombinationTemplate(@onNull StreamTemplate[] streamTemplates, @NonNull String description)366         public StreamCombinationTemplate(@NonNull StreamTemplate[] streamTemplates,
367                 @NonNull String description) {
368             this(streamTemplates, description, /*reprocessType*/ReprocessType.NONE);
369         }
370 
StreamCombinationTemplate(@onNull StreamTemplate[] streamTemplates, @NonNull String description, ReprocessType reprocessType)371         public StreamCombinationTemplate(@NonNull StreamTemplate[] streamTemplates,
372                 @NonNull String description, ReprocessType reprocessType) {
373             this(streamTemplates, description, reprocessType, /*substituteYUV*/ false);
374         }
375 
StreamCombinationTemplate(@onNull StreamTemplate[] streamTemplates, @NonNull String description, boolean substituteYUV)376         public StreamCombinationTemplate(@NonNull StreamTemplate[] streamTemplates,
377                 @NonNull String description, boolean substituteYUV) {
378             this(streamTemplates, description, /*reprocessType*/ ReprocessType.NONE,
379                     substituteYUV);
380         }
381 
StreamCombinationTemplate(@onNull StreamTemplate[] streamTemplates, @NonNull String description, ReprocessType reprocessType, boolean substituteYUV)382         public StreamCombinationTemplate(@NonNull StreamTemplate[] streamTemplates,
383                 @NonNull String description, ReprocessType reprocessType, boolean substituteYUV) {
384             mStreamTemplates = streamTemplates;
385             mReprocessType = reprocessType;
386             mDescription = description;
387             mSubstituteYUV = substituteYUV;
388         }
389     }
390 
391     private static StreamCombinationTemplate sLegacyCombinations[] = {
392         new StreamCombinationTemplate(new StreamTemplate [] {
393                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.MAXIMUM) },
394                 "Simple preview, GPU video processing, or no-preview video recording"),
395         new StreamCombinationTemplate(new StreamTemplate [] {
396                 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM) },
397                 "No-viewfinder still image capture"),
398         new StreamCombinationTemplate(new StreamTemplate [] {
399                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.MAXIMUM) },
400                 "In-application video/image processing"),
401         new StreamCombinationTemplate(new StreamTemplate [] {
402                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
403                 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM) },
404                 "Standard still imaging"),
405         new StreamCombinationTemplate(new StreamTemplate [] {
406                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
407                 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM) },
408                 "In-app processing plus still capture"),
409         new StreamCombinationTemplate(new StreamTemplate [] {
410                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
411                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW) },
412                 "Standard recording"),
413         new StreamCombinationTemplate(new StreamTemplate [] {
414                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
415                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW) },
416                 "Preview plus in-app processing"),
417         new StreamCombinationTemplate(new StreamTemplate [] {
418                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
419                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
420                 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM) },
421                 "Still capture plus in-app processing")
422     };
423 
424     private static StreamCombinationTemplate sLimitedCombinations[] = {
425         new StreamCombinationTemplate(new StreamTemplate [] {
426                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
427                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.RECORD)},
428                 "High-resolution video recording with preview"),
429         new StreamCombinationTemplate(new StreamTemplate [] {
430                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
431                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.RECORD)},
432                 "High-resolution in-app video processing with preview"),
433         new StreamCombinationTemplate(new StreamTemplate [] {
434                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
435                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.RECORD) },
436                 "Two-input in-app video processing"),
437         new StreamCombinationTemplate(new StreamTemplate [] {
438                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
439                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.RECORD),
440                 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.RECORD) },
441                 "High-resolution recording with video snapshot"),
442         new StreamCombinationTemplate(new StreamTemplate [] {
443                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
444                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.RECORD),
445                 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.RECORD) },
446                 "High-resolution in-app processing with video snapshot"),
447         new StreamCombinationTemplate(new StreamTemplate [] {
448                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
449                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
450                 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM) },
451                 "Two-input in-app processing with still capture")
452     };
453 
454     private static StreamCombinationTemplate sBurstCombinations[] = {
455         new StreamCombinationTemplate(new StreamTemplate [] {
456                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
457                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.MAXIMUM) },
458                 "Maximum-resolution GPU processing with preview"),
459         new StreamCombinationTemplate(new StreamTemplate [] {
460                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
461                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.MAXIMUM) },
462                 "Maximum-resolution in-app processing with preview"),
463         new StreamCombinationTemplate(new StreamTemplate [] {
464                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
465                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.MAXIMUM) },
466                 "Maximum-resolution two-input in-app processsing")
467     };
468 
469     private static StreamCombinationTemplate sFullCombinations[] = {
470         new StreamCombinationTemplate(new StreamTemplate [] {
471                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.MAXIMUM),
472                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.MAXIMUM) },
473                 "Maximum-resolution GPU processing with preview"),
474         new StreamCombinationTemplate(new StreamTemplate [] {
475                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.MAXIMUM),
476                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.MAXIMUM) },
477                 "Maximum-resolution in-app processing with preview"),
478         new StreamCombinationTemplate(new StreamTemplate [] {
479                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.MAXIMUM),
480                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.MAXIMUM) },
481                 "Maximum-resolution two-input in-app processsing"),
482         new StreamCombinationTemplate(new StreamTemplate [] {
483                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
484                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
485                 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM) },
486                 "Video recording with maximum-size video snapshot"),
487         new StreamCombinationTemplate(new StreamTemplate [] {
488                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.VGA),
489                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
490                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.MAXIMUM) },
491                 "Standard video recording plus maximum-resolution in-app processing"),
492         new StreamCombinationTemplate(new StreamTemplate [] {
493                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.VGA),
494                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
495                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.MAXIMUM) },
496                 "Preview plus two-input maximum-resolution in-app processing")
497     };
498 
499     private static StreamCombinationTemplate sRawCombinations[] = {
500         new StreamCombinationTemplate(new StreamTemplate [] {
501                 new StreamTemplate(ImageFormat.RAW_SENSOR,  SizeThreshold.MAXIMUM) },
502                 "No-preview DNG capture"),
503         new StreamCombinationTemplate(new StreamTemplate [] {
504                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
505                 new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.MAXIMUM) },
506                 "Standard DNG capture"),
507         new StreamCombinationTemplate(new StreamTemplate [] {
508                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
509                 new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.MAXIMUM) },
510                 "In-app processing plus DNG capture"),
511         new StreamCombinationTemplate(new StreamTemplate [] {
512                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
513                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
514                 new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.MAXIMUM) },
515                 "Video recording with DNG capture"),
516         new StreamCombinationTemplate(new StreamTemplate [] {
517                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
518                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
519                 new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.MAXIMUM) },
520                 "Preview with in-app processing and DNG capture"),
521         new StreamCombinationTemplate(new StreamTemplate [] {
522                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
523                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
524                 new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.MAXIMUM) },
525                 "Two-input in-app processing plus DNG capture"),
526         new StreamCombinationTemplate(new StreamTemplate [] {
527                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
528                 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM),
529                 new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.MAXIMUM) },
530                 "Still capture with simultaneous JPEG and DNG"),
531         new StreamCombinationTemplate(new StreamTemplate [] {
532                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
533                 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM),
534                 new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.MAXIMUM) },
535                 "In-app processing with simultaneous JPEG and DNG")
536     };
537 
538     private static StreamCombinationTemplate sLevel3Combinations[] = {
539         new StreamCombinationTemplate(new StreamTemplate [] {
540                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
541                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.VGA),
542                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.MAXIMUM),
543                 new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.MAXIMUM) },
544                 "In-app viewfinder analysis with dynamic selection of output format"),
545         new StreamCombinationTemplate(new StreamTemplate [] {
546                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
547                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.VGA),
548                 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM),
549                 new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.MAXIMUM) },
550                 "In-app viewfinder analysis with dynamic selection of output format")
551     };
552 
553     private static StreamCombinationTemplate sLimitedPrivateReprocCombinations[] = {
554         new StreamCombinationTemplate(new StreamTemplate [] {
555                 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM) },
556                 "No-viewfinder still image reprocessing",
557                 /*reprocessType*/ ReprocessType.PRIVATE),
558         new StreamCombinationTemplate(new StreamTemplate [] {
559                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
560                 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM) },
561                 "ZSL(Zero-Shutter-Lag) still imaging",
562                 /*reprocessType*/ ReprocessType.PRIVATE),
563         new StreamCombinationTemplate(new StreamTemplate [] {
564                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
565                 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM) },
566                 "ZSL still and in-app processing imaging",
567                 /*reprocessType*/ ReprocessType.PRIVATE),
568         new StreamCombinationTemplate(new StreamTemplate [] {
569                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
570                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
571                 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM) },
572                 "ZSL in-app processing with still capture",
573                 /*reprocessType*/ ReprocessType.PRIVATE),
574     };
575 
576     private static StreamCombinationTemplate sLimitedYUVReprocCombinations[] = {
577         new StreamCombinationTemplate(new StreamTemplate [] {
578                 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM) },
579                 "No-viewfinder still image reprocessing",
580                 /*reprocessType*/ ReprocessType.YUV),
581         new StreamCombinationTemplate(new StreamTemplate [] {
582                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
583                 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM) },
584                 "ZSL(Zero-Shutter-Lag) still imaging",
585                 /*reprocessType*/ ReprocessType.YUV),
586         new StreamCombinationTemplate(new StreamTemplate [] {
587                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
588                 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM) },
589                 "ZSL still and in-app processing imaging",
590                 /*reprocessType*/ ReprocessType.YUV),
591         new StreamCombinationTemplate(new StreamTemplate [] {
592                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
593                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
594                 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM) },
595                 "ZSL in-app processing with still capture",
596                 /*reprocessType*/ ReprocessType.YUV),
597     };
598 
599     private static StreamCombinationTemplate sFullPrivateReprocCombinations[] = {
600         new StreamCombinationTemplate(new StreamTemplate [] {
601                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
602                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.RECORD) },
603                 "High-resolution ZSL in-app video processing with regular preview",
604                 /*reprocessType*/ ReprocessType.PRIVATE),
605         new StreamCombinationTemplate(new StreamTemplate [] {
606                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
607                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.MAXIMUM) },
608                 "Maximum-resolution ZSL in-app processing with regular preview",
609                 /*reprocessType*/ ReprocessType.PRIVATE),
610         new StreamCombinationTemplate(new StreamTemplate [] {
611                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
612                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.MAXIMUM) },
613                 "Maximum-resolution two-input ZSL in-app processing",
614                 /*reprocessType*/ ReprocessType.PRIVATE),
615         new StreamCombinationTemplate(new StreamTemplate [] {
616                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
617                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
618                 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM) },
619                 "ZSL still capture and in-app processing",
620                 /*reprocessType*/ ReprocessType.PRIVATE),
621     };
622 
623     private static StreamCombinationTemplate sFullYUVReprocCombinations[] = {
624         new StreamCombinationTemplate(new StreamTemplate [] {
625                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW) },
626                 "Maximum-resolution multi-frame image fusion in-app processing with regular "
627                 + "preview",
628                 /*reprocessType*/ ReprocessType.YUV),
629         new StreamCombinationTemplate(new StreamTemplate [] {
630                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW) },
631                 "Maximum-resolution multi-frame image fusion two-input in-app processing",
632                 /*reprocessType*/ ReprocessType.YUV),
633         new StreamCombinationTemplate(new StreamTemplate [] {
634                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
635                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.RECORD) },
636                 "High-resolution ZSL in-app video processing with regular preview",
637                 /*reprocessType*/ ReprocessType.YUV),
638         new StreamCombinationTemplate(new StreamTemplate [] {
639                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
640                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
641                 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM) },
642                 "ZSL still capture and in-app processing",
643                 /*reprocessType*/ ReprocessType.YUV),
644     };
645 
646     private static StreamCombinationTemplate sRAWPrivateReprocCombinations[] = {
647         new StreamCombinationTemplate(new StreamTemplate [] {
648                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
649                 new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.MAXIMUM) },
650                 "Mutually exclusive ZSL in-app processing and DNG capture",
651                 /*reprocessType*/ ReprocessType.PRIVATE),
652         new StreamCombinationTemplate(new StreamTemplate [] {
653                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
654                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
655                 new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.MAXIMUM) },
656                 "Mutually exclusive ZSL in-app processing and preview with DNG capture",
657                 /*reprocessType*/ ReprocessType.PRIVATE),
658         new StreamCombinationTemplate(new StreamTemplate [] {
659                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
660                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
661                 new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.MAXIMUM) },
662                 "Mutually exclusive ZSL two-input in-app processing and DNG capture",
663                 /*reprocessType*/ ReprocessType.PRIVATE),
664         new StreamCombinationTemplate(new StreamTemplate [] {
665                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
666                 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM),
667                 new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.MAXIMUM) },
668                 "Mutually exclusive ZSL still capture and preview with DNG capture",
669                 /*reprocessType*/ ReprocessType.PRIVATE),
670         new StreamCombinationTemplate(new StreamTemplate [] {
671                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
672                 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM),
673                 new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.MAXIMUM) },
674                 "Mutually exclusive ZSL in-app processing with still capture and DNG capture",
675                 /*reprocessType*/ ReprocessType.PRIVATE),
676     };
677 
678     private static StreamCombinationTemplate sRAWYUVReprocCombinations[] = {
679         new StreamCombinationTemplate(new StreamTemplate [] {
680                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
681                 new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.MAXIMUM) },
682                 "Mutually exclusive ZSL in-app processing and DNG capture",
683                 /*reprocessType*/ ReprocessType.YUV),
684         new StreamCombinationTemplate(new StreamTemplate [] {
685                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
686                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
687                 new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.MAXIMUM) },
688                 "Mutually exclusive ZSL in-app processing and preview with DNG capture",
689                 /*reprocessType*/ ReprocessType.YUV),
690         new StreamCombinationTemplate(new StreamTemplate [] {
691                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
692                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
693                 new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.MAXIMUM) },
694                 "Mutually exclusive ZSL two-input in-app processing and DNG capture",
695                 /*reprocessType*/ ReprocessType.YUV),
696         new StreamCombinationTemplate(new StreamTemplate [] {
697                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
698                 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM),
699                 new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.MAXIMUM) },
700                 "Mutually exclusive ZSL still capture and preview with DNG capture",
701                 /*reprocessType*/ ReprocessType.YUV),
702         new StreamCombinationTemplate(new StreamTemplate [] {
703                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
704                 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM),
705                 new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.MAXIMUM) },
706                 "Mutually exclusive ZSL in-app processing with still capture and DNG capture",
707                 /*reprocessType*/ ReprocessType.YUV),
708     };
709 
710     private static StreamCombinationTemplate sLevel3PrivateReprocCombinations[] = {
711         new StreamCombinationTemplate(new StreamTemplate [] {
712                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
713                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.VGA),
714                 new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.MAXIMUM),
715                 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM) },
716                 "In-app viewfinder analysis with ZSL, RAW, and JPEG reprocessing output",
717                 /*reprocessType*/ ReprocessType.PRIVATE),
718     };
719 
720     private static StreamCombinationTemplate sLevel3YUVReprocCombinations[] = {
721         new StreamCombinationTemplate(new StreamTemplate [] {
722                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
723                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.VGA),
724                 new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.MAXIMUM) },
725                 "In-app viewfinder analysis with ZSL and RAW",
726                 /*reprocessType*/ ReprocessType.YUV),
727         new StreamCombinationTemplate(new StreamTemplate [] {
728                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
729                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.VGA),
730                 new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.MAXIMUM),
731                 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM) },
732                 "In-app viewfinder analysis with ZSL, RAW, and JPEG reprocessing output",
733                 /*reprocessType*/ ReprocessType.YUV),
734     };
735 
736     private static StreamCombinationTemplate sConcurrentStreamCombinations[] = {
737         new StreamCombinationTemplate(new StreamTemplate [] {
738                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.s1440p) },
739                 "In-app video / image processing"),
740         new StreamCombinationTemplate(new StreamTemplate [] {
741                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.s1440p) },
742                 "preview / preview to GPU"),
743         new StreamCombinationTemplate(new StreamTemplate [] {
744                 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.s1440p) },
745                 "No view-finder still image capture"),
746         new StreamCombinationTemplate(new StreamTemplate [] {
747                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.s720p),
748                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.s1440p)},
749                 "Two-input in app video / image processing"),
750         new StreamCombinationTemplate(new StreamTemplate [] {
751                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.s720p),
752                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.s1440p)},
753                 "High resolution video recording with preview"),
754         new StreamCombinationTemplate(new StreamTemplate [] {
755                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.s720p),
756                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.s1440p)},
757                 "In-app video / image processing with preview"),
758         new StreamCombinationTemplate(new StreamTemplate [] {
759                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.s720p),
760                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.s1440p)},
761                 "In-app video / image processing with preview"),
762         new StreamCombinationTemplate(new StreamTemplate [] {
763                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.s720p),
764                 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.s1440p)},
765                 "Standard stil image capture"),
766         new StreamCombinationTemplate(new StreamTemplate [] {
767                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.s720p),
768                 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.s1440p)},
769                 "Standard still image capture"),
770     };
771 
772     private static StreamCombinationTemplate sConcurrentDepthOnlyStreamCombinations[] = {
773         new StreamCombinationTemplate(new StreamTemplate [] {
774                 new StreamTemplate(ImageFormat.DEPTH16, SizeThreshold.VGA) },
775                 "Depth capture for mesh based object rendering"),
776     };
777 
778     private static StreamCombinationTemplate sUltraHighResolutionStreamCombinations[] = {
779         // UH res YUV / RAW / JPEG + PRIV preview size stream
780         new StreamCombinationTemplate(new StreamTemplate [] {
781                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.FULL_RES),
782                  new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW)},
783                 "Ultra high resolution YUV image capture with preview"),
784         new StreamCombinationTemplate(new StreamTemplate [] {
785                 new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.FULL_RES),
786                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW)},
787                 "Ultra high resolution RAW_SENSOR image capture with preview"),
788         new StreamCombinationTemplate(new StreamTemplate [] {
789                 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.FULL_RES),
790                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW)},
791                 "Ultra high resolution JPEG image capture with preview"),
792 
793         // UH res YUV / RAW / JPEG + YUV preview size stream
794         new StreamCombinationTemplate(new StreamTemplate [] {
795                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.FULL_RES),
796                  new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW)},
797                 "No-viewfinder Ultra high resolution YUV image capture with image analysis"),
798         new StreamCombinationTemplate(new StreamTemplate [] {
799                 new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.FULL_RES),
800                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW)},
801                 "No-viewfinder Ultra high resolution RAW_SENSOR image capture with image analysis"),
802         new StreamCombinationTemplate(new StreamTemplate [] {
803                 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.FULL_RES),
804                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW)},
805                 "No-viewfinder Ultra high resolution JPEG image capture with image analysis"),
806 
807         // UH res YUV / RAW / JPEG + PRIV preview + PRIV RECORD stream
808         new StreamCombinationTemplate(new StreamTemplate [] {
809                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.FULL_RES),
810                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
811                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.RECORD)},
812                 "Ultra high resolution YUV image capture with preview + app-based image analysis"),
813         new StreamCombinationTemplate(new StreamTemplate [] {
814                 new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.FULL_RES),
815                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
816                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.RECORD)},
817                 "Ultra high resolution RAW image capture with preview + app-based image analysis"),
818         new StreamCombinationTemplate(new StreamTemplate [] {
819                 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.FULL_RES),
820                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
821                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.RECORD)},
822                 "Ultra high resolution JPEG image capture with preview + app-based image analysis"),
823 
824         // UH res YUV / RAW / JPEG + PRIV preview + YUV RECORD stream
825         new StreamCombinationTemplate(new StreamTemplate [] {
826                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.FULL_RES),
827                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
828                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.RECORD)},
829                 "Ultra high resolution YUV image capture with preview + app-based image analysis"),
830         new StreamCombinationTemplate(new StreamTemplate [] {
831                 new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.FULL_RES),
832                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
833                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.RECORD)},
834                 "Ultra high resolution RAW image capture with preview + app-based image analysis"),
835         new StreamCombinationTemplate(new StreamTemplate [] {
836                 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.FULL_RES),
837                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
838                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.RECORD)},
839                 "Ultra high resolution JPEG image capture with preview + app-based image analysis"),
840 
841         // UH RES YUV / RAW / JPEG + PRIV preview + YUV / RAW / JPEG Maximum stream
842         new StreamCombinationTemplate(new StreamTemplate [] {
843                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.FULL_RES),
844                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
845                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.MAXIMUM)},
846                 "Ultra high resolution YUV image capture with preview + default",
847                 /*substituteYUV*/ true),
848         new StreamCombinationTemplate(new StreamTemplate [] {
849                 new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.FULL_RES),
850                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
851                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.MAXIMUM)},
852                 "Ultra high resolution RAW image capture with preview + default",
853                 /*substituteYUV*/ true),
854         new StreamCombinationTemplate(new StreamTemplate [] {
855                 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.FULL_RES),
856                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
857                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.MAXIMUM)},
858                 "Ultra high resolution JPEG capture with preview + default",
859                 /*substituteYUV*/ true),
860     };
861 
862     private static StreamCombinationTemplate sUltraHighResolutionReprocStreamCombinations[] = {
863         // RAW_SENSOR -> RAW_SENSOR + preview size PRIV / YUV
864         new StreamCombinationTemplate(new StreamTemplate [] {
865                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW)},
866                 "In-app RAW remosaic reprocessing with separate preview",
867                 /*reprocessType*/ ReprocessType.REMOSAIC),
868         new StreamCombinationTemplate(new StreamTemplate [] {
869                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW)},
870                 "In-app RAW remosaic reprocessing with in-app image analysis",
871                 /*reprocessType*/ ReprocessType.REMOSAIC),
872 
873         // RAW -> JPEG / YUV reprocessing + YUV / PRIV preview size stream
874         new StreamCombinationTemplate(new StreamTemplate [] {
875                 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.FULL_RES),
876                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW)},
877                 "In-app RAW -> JPEG reprocessing with separate preview",
878                 /*reprocessType*/ ReprocessType.REMOSAIC),
879         new StreamCombinationTemplate(new StreamTemplate [] {
880                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.FULL_RES),
881                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW)},
882                 "In-app RAW -> YUV reprocessing with separate preview",
883                 /*reprocessType*/ ReprocessType.REMOSAIC),
884         new StreamCombinationTemplate(new StreamTemplate [] {
885                 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.FULL_RES),
886                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW)},
887                 "In-app RAW -> JPEG reprocessing with in-app image analysis",
888                 /*reprocessType*/ ReprocessType.REMOSAIC),
889         new StreamCombinationTemplate(new StreamTemplate [] {
890                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.FULL_RES),
891                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW)},
892                 "In-app RAW -> YUV reprocessing with in-app image analysis",
893                 /*reprocessType*/ ReprocessType.REMOSAIC),
894     };
895 
896     private static StreamCombinationTemplate sUltraHighResolutionYUVReprocStreamCombinations[] = {
897         // YUV -> JPEG reprocess + PRIV / YUV preview size stream
898         new StreamCombinationTemplate(new StreamTemplate [] {
899                 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.FULL_RES),
900                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW)},
901                 "Ultra high resolution YUV -> JPEG reprocessing with separate preview",
902                 /*reprocessType*/ ReprocessType.YUV),
903         new StreamCombinationTemplate(new StreamTemplate [] {
904                 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.FULL_RES),
905                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW)},
906                 "Ultra high resolution YUV -> JPEG reprocessing with in-app image analysis",
907                 /*reprocessType*/ ReprocessType.YUV),
908 
909         // YUV -> YUV reprocess + PRIV / YUV preview size stream
910         new StreamCombinationTemplate(new StreamTemplate [] {
911                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.FULL_RES),
912                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW)},
913                 "Ultra high resolution YUV -> YUV reprocessing with separate preview",
914                 /*reprocessType*/ ReprocessType.YUV),
915         new StreamCombinationTemplate(new StreamTemplate [] {
916                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.FULL_RES),
917                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW)},
918                 "Ultra high resolution YUV -> YUV reprocessing with in-app image analysis",
919                 /*reprocessType*/ ReprocessType.YUV),
920     };
921 
922     private static StreamCombinationTemplate sUltraHighResolutionPRIVReprocStreamCombinations[] = {
923         new StreamCombinationTemplate(new StreamTemplate [] {
924                 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.FULL_RES),
925                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW)},
926                 "Ultra high resolution PRIVATE -> JPEG reprocessing with separate preview",
927                 /*reprocessType*/ ReprocessType.PRIVATE),
928         new StreamCombinationTemplate(new StreamTemplate [] {
929                 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.FULL_RES),
930                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW)},
931                 "Ultra high resolution PRIVATE -> JPEG reprocessing with in-app image analysis",
932                 /*reprocessType*/ ReprocessType.PRIVATE),
933     };
934 
935     /**
936      * Helper builder class to generate a list of available mandatory stream combinations.
937      * @hide
938      */
939     public static final class Builder {
940         private Size mDisplaySize;
941         private List<Integer> mCapabilities;
942         private int mHwLevel, mCameraId;
943         private StreamConfigurationMap mStreamConfigMap;
944         private StreamConfigurationMap mStreamConfigMapMaximumResolution;
945         private boolean mIsHiddenPhysicalCamera;
946 
947         private final Size kPreviewSizeBound = new Size(1920, 1088);
948 
949         /**
950          * Helper class to be used to generate the available mandatory stream combinations.
951          *
952          * @param cameraId Current camera id.
953          * @param hwLevel The camera HW level as reported by android.info.supportedHardwareLevel.
954          * @param displaySize The device display size.
955          * @param capabilities The camera device capabilities.
956          * @param sm The camera device stream configuration map.
957          * @param smMaxResolution The camera device stream configuration map when it runs in max
958          *                        resolution mode.
959          */
Builder(int cameraId, int hwLevel, @NonNull Size displaySize, @NonNull List<Integer> capabilities, @NonNull StreamConfigurationMap sm, StreamConfigurationMap smMaxResolution)960         public Builder(int cameraId, int hwLevel, @NonNull Size displaySize,
961                 @NonNull List<Integer> capabilities, @NonNull StreamConfigurationMap sm,
962                 StreamConfigurationMap smMaxResolution) {
963             mCameraId = cameraId;
964             mDisplaySize = displaySize;
965             mCapabilities = capabilities;
966             mStreamConfigMap = sm;
967             mStreamConfigMapMaximumResolution = smMaxResolution;
968             mHwLevel = hwLevel;
969             mIsHiddenPhysicalCamera =
970                     CameraManager.isHiddenPhysicalCamera(Integer.toString(mCameraId));
971         }
972 
973         /**
974           * Retrieve a list of all available mandatory concurrent stream combinations.
975           * This method should only be called for devices which are listed in combinations returned
976           * by CameraManager.getConcurrentCameraIds.
977           *
978           * @return a non-modifiable list of supported mandatory concurrent stream combinations.
979           */
980         public @NonNull List<MandatoryStreamCombination>
getAvailableMandatoryConcurrentStreamCombinations()981                 getAvailableMandatoryConcurrentStreamCombinations() {
982             // Since concurrent streaming support is optional, we mandate these stream
983             // combinations regardless of camera device capabilities.
984 
985             StreamCombinationTemplate []chosenStreamCombinations = sConcurrentStreamCombinations;
986             if (!isColorOutputSupported()) {
987                 Log.v(TAG, "Device is not backward compatible, depth streams are mandatory!");
988                 chosenStreamCombinations = sConcurrentDepthOnlyStreamCombinations;
989             }
990             Size sizeVGAp = new Size(640, 480);
991             Size size720p = new Size(1280, 720);
992             Size size1440p = new Size(1920, 1440);
993 
994             ArrayList<MandatoryStreamCombination> availableConcurrentStreamCombinations =
995                     new ArrayList<MandatoryStreamCombination>();
996             availableConcurrentStreamCombinations.ensureCapacity(
997                     chosenStreamCombinations.length);
998             for (StreamCombinationTemplate combTemplate : chosenStreamCombinations) {
999                 ArrayList<MandatoryStreamInformation> streamsInfo =
1000                         new ArrayList<MandatoryStreamInformation>();
1001                 streamsInfo.ensureCapacity(combTemplate.mStreamTemplates.length);
1002                 for (StreamTemplate template : combTemplate.mStreamTemplates) {
1003                     MandatoryStreamInformation streamInfo;
1004                     List<Size> sizes = new ArrayList<Size>();
1005                     Size formatSize = null;
1006                     switch (template.mSizeThreshold) {
1007                         case s1440p:
1008                             formatSize = size1440p;
1009                             break;
1010                         case VGA:
1011                             formatSize = sizeVGAp;
1012                             break;
1013                         default:
1014                             formatSize = size720p;
1015                     }
1016                     Size sizeChosen =
1017                             getMinSize(formatSize,
1018                                     getMaxSize(mStreamConfigMap.getOutputSizes(template.mFormat)));
1019                     sizes.add(sizeChosen);
1020                     try {
1021                         streamInfo = new MandatoryStreamInformation(sizes, template.mFormat,
1022                             /*isMaximumSize*/false);
1023                     } catch (IllegalArgumentException e) {
1024                         String cause = "No available sizes found for format: " + template.mFormat
1025                                 + " size threshold: " + template.mSizeThreshold + " combination: "
1026                                 + combTemplate.mDescription;
1027                         throw new RuntimeException(cause, e);
1028                     }
1029                     streamsInfo.add(streamInfo);
1030                 }
1031 
1032                 MandatoryStreamCombination streamCombination;
1033                 try {
1034                     streamCombination = new MandatoryStreamCombination(streamsInfo,
1035                             combTemplate.mDescription, /*isReprocess*/false);
1036                 } catch (IllegalArgumentException e) {
1037                     String cause =  "No stream information for mandatory combination: "
1038                             + combTemplate.mDescription;
1039                     throw new RuntimeException(cause, e);
1040                 }
1041                 availableConcurrentStreamCombinations.add(streamCombination);
1042             }
1043             return Collections.unmodifiableList(availableConcurrentStreamCombinations);
1044         }
1045 
1046         /**
1047          * Retrieve a list of all available mandatory stream combinations supported when
1048          * {@link CaptureRequest#ANDROID_SENSOR_PIXEL_MODE} is set to
1049          * {@link CameraMetadata#ANDROID_SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION}.
1050          *
1051          * @return a non-modifiable list of supported mandatory stream combinations or
1052          *         null in case device is not backward compatible or the method encounters
1053          *         an error.
1054          */
1055         public @NonNull List<MandatoryStreamCombination>
getAvailableMandatoryMaximumResolutionStreamCombinations()1056                 getAvailableMandatoryMaximumResolutionStreamCombinations() {
1057 
1058             if (!isColorOutputSupported()) {
1059                 Log.v(TAG, "Device is not backward compatible!, no mandatory maximum res streams");
1060                 return null;
1061             }
1062 
1063             ArrayList<StreamCombinationTemplate> chosenStreamCombinationTemplates =
1064                     new ArrayList<StreamCombinationTemplate>();
1065 
1066             chosenStreamCombinationTemplates.addAll(
1067                     Arrays.asList(sUltraHighResolutionStreamCombinations));
1068 
1069             ArrayList<MandatoryStreamCombination> availableStreamCombinations =
1070                     new ArrayList<MandatoryStreamCombination>();
1071             boolean addRemosaicReprocessing = isRemosaicReprocessingSupported();
1072 
1073             int remosaicSize = 0;
1074             Size [] maxResYUVInputSizes =
1075                     mStreamConfigMapMaximumResolution.getInputSizes(ImageFormat.YUV_420_888);
1076             Size [] maxResPRIVInputSizes =
1077                     mStreamConfigMapMaximumResolution.getInputSizes(ImageFormat.PRIVATE);
1078 
1079             if (addRemosaicReprocessing) {
1080                 remosaicSize += sUltraHighResolutionReprocStreamCombinations.length;
1081                 chosenStreamCombinationTemplates.addAll(
1082                         Arrays.asList(sUltraHighResolutionReprocStreamCombinations));
1083             }
1084 
1085             if (maxResYUVInputSizes != null && maxResYUVInputSizes.length != 0) {
1086                 remosaicSize += sUltraHighResolutionYUVReprocStreamCombinations.length;
1087                 chosenStreamCombinationTemplates.addAll(
1088                         Arrays.asList(sUltraHighResolutionYUVReprocStreamCombinations));
1089             }
1090 
1091             if (maxResPRIVInputSizes != null && maxResPRIVInputSizes.length != 0) {
1092                 remosaicSize += sUltraHighResolutionPRIVReprocStreamCombinations.length;
1093                 chosenStreamCombinationTemplates.addAll(
1094                         Arrays.asList(sUltraHighResolutionPRIVReprocStreamCombinations));
1095 
1096             }
1097             availableStreamCombinations.ensureCapacity(
1098                     chosenStreamCombinationTemplates.size() + remosaicSize);
1099             fillUHMandatoryStreamCombinations(availableStreamCombinations,
1100                     chosenStreamCombinationTemplates);
1101 
1102             return Collections.unmodifiableList(availableStreamCombinations);
1103         }
1104 
createUHSensorMandatoryStreamCombination( StreamCombinationTemplate combTemplate, int substitutedFormat)1105         private MandatoryStreamCombination createUHSensorMandatoryStreamCombination(
1106                 StreamCombinationTemplate combTemplate, int substitutedFormat) {
1107             ArrayList<MandatoryStreamInformation> streamsInfo =
1108                     new ArrayList<MandatoryStreamInformation>();
1109             streamsInfo.ensureCapacity(combTemplate.mStreamTemplates.length);
1110             boolean isReprocess = combTemplate.mReprocessType != ReprocessType.NONE;
1111             if (isReprocess) {
1112                 int format = -1;
1113                 ArrayList<Size> inputSize = new ArrayList<Size>();
1114                 if (combTemplate.mReprocessType == ReprocessType.PRIVATE) {
1115                     inputSize.add(
1116                             getMaxSize(mStreamConfigMapMaximumResolution.getInputSizes(
1117                                     ImageFormat.PRIVATE)));
1118                     format = ImageFormat.PRIVATE;
1119                 } else if (combTemplate.mReprocessType == ReprocessType.REMOSAIC) {
1120                     inputSize.add(
1121                             getMaxSize(mStreamConfigMapMaximumResolution.getInputSizes(
1122                                     ImageFormat.RAW_SENSOR)));
1123                     format = ImageFormat.RAW_SENSOR;
1124                 } else {
1125                     inputSize.add(
1126                             getMaxSize(mStreamConfigMapMaximumResolution.getInputSizes(
1127                                     ImageFormat.YUV_420_888)));
1128                     format = ImageFormat.YUV_420_888;
1129                 }
1130                 streamsInfo.add(new MandatoryStreamInformation(inputSize, format,
1131                         /*isMaximumSize*/false, /*isInput*/true,
1132                         /*isUltraHighResolution*/ true));
1133                 streamsInfo.add(new MandatoryStreamInformation(inputSize, format,
1134                         /*isMaximumSize*/false, /*isInput*/ false,
1135                         /*isUltraHighResolution*/true));
1136             }
1137             HashMap<Pair<SizeThreshold, Integer>, List<Size>> availableDefaultNonRawSizes =
1138                     enumerateAvailableSizes();
1139             if (availableDefaultNonRawSizes == null) {
1140                 Log.e(TAG, "Available size enumeration failed");
1141                 return null;
1142             }
1143             Size[] defaultRawSizes =
1144                     mStreamConfigMap.getOutputSizes(ImageFormat.RAW_SENSOR);
1145             ArrayList<Size> availableDefaultRawSizes = new ArrayList<>();
1146             if (defaultRawSizes != null) {
1147                 availableDefaultRawSizes.ensureCapacity(defaultRawSizes.length);
1148                 availableDefaultRawSizes.addAll(Arrays.asList(defaultRawSizes));
1149             }
1150             for (StreamTemplate template : combTemplate.mStreamTemplates) {
1151                 MandatoryStreamInformation streamInfo;
1152                 List<Size> sizes = new ArrayList<Size>();
1153                 int formatChosen = template.mFormat;
1154                 boolean isUltraHighResolution =
1155                         (template.mSizeThreshold == SizeThreshold.FULL_RES);
1156                 StreamConfigurationMap sm =
1157                         isUltraHighResolution ?
1158                                 mStreamConfigMapMaximumResolution : mStreamConfigMap;
1159                 boolean isMaximumSize = (template.mSizeThreshold == SizeThreshold.MAXIMUM);
1160 
1161                 if (substitutedFormat != ImageFormat.UNKNOWN && isMaximumSize) {
1162                     formatChosen = substitutedFormat;
1163                 }
1164 
1165                 if (isUltraHighResolution) {
1166                     sizes.add(getMaxSize(sm.getOutputSizes(formatChosen)));
1167                 } else {
1168                     if (formatChosen == ImageFormat.RAW_SENSOR) {
1169                         // RAW_SENSOR always has MAXIMUM threshold.
1170                         sizes = availableDefaultRawSizes;
1171                     } else {
1172                         Pair<SizeThreshold, Integer> pair =
1173                             new Pair<SizeThreshold, Integer>(template.mSizeThreshold,
1174                                     new Integer(formatChosen));
1175                         sizes = availableDefaultNonRawSizes.get(pair);
1176                     }
1177                 }
1178 
1179                 try {
1180                     streamInfo = new MandatoryStreamInformation(sizes, formatChosen,
1181                             isMaximumSize, /*isInput*/ false, isUltraHighResolution);
1182                 } catch (IllegalArgumentException e) {
1183                     String cause = "No available sizes found for format: " + template.mFormat
1184                             + " size threshold: " + template.mSizeThreshold + " combination: "
1185                             + combTemplate.mDescription;
1186                     throw new RuntimeException(cause, e);
1187                 }
1188                 streamsInfo.add(streamInfo);
1189             }
1190 
1191             String formatString = null;
1192             switch (substitutedFormat) {
1193                 case ImageFormat.RAW_SENSOR :
1194                     formatString = "RAW_SENSOR";
1195                     break;
1196                 case ImageFormat.JPEG :
1197                     formatString = "JPEG";
1198                     break;
1199                 default:
1200                     formatString = "YUV";
1201             }
1202 
1203             MandatoryStreamCombination streamCombination;
1204             try {
1205                 streamCombination = new MandatoryStreamCombination(streamsInfo,
1206                         combTemplate.mDescription + " " + formatString + " still-capture",
1207                         isReprocess);
1208             } catch (IllegalArgumentException e) {
1209                 String cause =  "No stream information for mandatory combination: "
1210                         + combTemplate.mDescription;
1211                 throw new RuntimeException(cause, e);
1212             }
1213             return streamCombination;
1214         }
1215 
fillUHMandatoryStreamCombinations( ArrayList<MandatoryStreamCombination> availableStreamCombinations, ArrayList<StreamCombinationTemplate> chosenTemplates)1216         private void fillUHMandatoryStreamCombinations(
1217                 ArrayList<MandatoryStreamCombination> availableStreamCombinations,
1218                 ArrayList<StreamCombinationTemplate> chosenTemplates) {
1219 
1220             for (StreamCombinationTemplate combTemplate : chosenTemplates) {
1221                 MandatoryStreamCombination streamCombination =
1222                         createUHSensorMandatoryStreamCombination(combTemplate,
1223                                   ImageFormat.UNKNOWN);
1224                 availableStreamCombinations.add(streamCombination);
1225                 if (combTemplate.mSubstituteYUV) {
1226                      streamCombination =
1227                             createUHSensorMandatoryStreamCombination(combTemplate,
1228                                     ImageFormat.RAW_SENSOR);
1229                     availableStreamCombinations.add(streamCombination);
1230                     streamCombination =
1231                             createUHSensorMandatoryStreamCombination(combTemplate,
1232                                     ImageFormat.JPEG);
1233                     availableStreamCombinations.add(streamCombination);
1234                 }
1235             }
1236         }
1237 
1238         /**
1239          * Retrieve a list of all available mandatory stream combinations.
1240          *
1241          * @return a non-modifiable list of supported mandatory stream combinations or
1242          *         null in case device is not backward compatible or the method encounters
1243          *         an error.
1244          */
1245         public @Nullable List<MandatoryStreamCombination>
getAvailableMandatoryStreamCombinations()1246             getAvailableMandatoryStreamCombinations() {
1247             if (!isColorOutputSupported()) {
1248                 Log.v(TAG, "Device is not backward compatible!");
1249                 return null;
1250             }
1251 
1252             if ((mCameraId < 0) && !isExternalCamera()) {
1253                 Log.i(TAG, "Invalid camera id");
1254                 return null;
1255             }
1256 
1257             ArrayList<StreamCombinationTemplate> availableTemplates =
1258                     new ArrayList<StreamCombinationTemplate> ();
1259             if (isHardwareLevelAtLeastLegacy()) {
1260                 availableTemplates.addAll(Arrays.asList(sLegacyCombinations));
1261             }
1262 
1263             // External devices are identical to limited devices w.r.t. stream combinations.
1264             if (isHardwareLevelAtLeastLimited() || isExternalCamera()) {
1265                 availableTemplates.addAll(Arrays.asList(sLimitedCombinations));
1266 
1267                 if (isPrivateReprocessingSupported()) {
1268                     availableTemplates.addAll(Arrays.asList(sLimitedPrivateReprocCombinations));
1269                 }
1270 
1271                 if (isYUVReprocessingSupported()) {
1272                     availableTemplates.addAll(Arrays.asList(sLimitedYUVReprocCombinations));
1273                 }
1274 
1275             }
1276 
1277             if (isCapabilitySupported(
1278                     CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE)) {
1279                 availableTemplates.addAll(Arrays.asList(sBurstCombinations));
1280             }
1281 
1282             if (isHardwareLevelAtLeastFull()) {
1283                 availableTemplates.addAll(Arrays.asList(sFullCombinations));
1284 
1285                 if (isPrivateReprocessingSupported()) {
1286                     availableTemplates.addAll(Arrays.asList(sFullPrivateReprocCombinations));
1287                 }
1288 
1289                 if (isYUVReprocessingSupported()) {
1290                     availableTemplates.addAll(Arrays.asList(sFullYUVReprocCombinations));
1291                 }
1292 
1293             }
1294 
1295             if (isCapabilitySupported(
1296                     CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW)) {
1297                 availableTemplates.addAll(Arrays.asList(sRawCombinations));
1298 
1299                 if (isPrivateReprocessingSupported()) {
1300                     availableTemplates.addAll(Arrays.asList(sRAWPrivateReprocCombinations));
1301                 }
1302 
1303                 if (isYUVReprocessingSupported()) {
1304                     availableTemplates.addAll(Arrays.asList(sRAWYUVReprocCombinations));
1305                 }
1306 
1307             }
1308 
1309             if (isHardwareLevelAtLeastLevel3()) {
1310                 availableTemplates.addAll(Arrays.asList(sLevel3Combinations));
1311 
1312                 if (isPrivateReprocessingSupported()) {
1313                     availableTemplates.addAll(Arrays.asList(sLevel3PrivateReprocCombinations));
1314                 }
1315 
1316                 if (isYUVReprocessingSupported()) {
1317                     availableTemplates.addAll(Arrays.asList(sLevel3YUVReprocCombinations));
1318                 }
1319 
1320             }
1321 
1322             return generateAvailableCombinations(availableTemplates);
1323         }
1324 
1325         /**
1326          * Helper method to generate the available stream combinations given the
1327          * list of available combination templates.
1328          *
1329          * @param availableTemplates a list of templates supported by the camera device.
1330          * @return a non-modifiable list of supported mandatory stream combinations or
1331          *         null in case of errors.
1332          */
generateAvailableCombinations( @onNull ArrayList<StreamCombinationTemplate> availableTemplates)1333         private @Nullable List<MandatoryStreamCombination> generateAvailableCombinations(
1334                 @NonNull ArrayList<StreamCombinationTemplate> availableTemplates) {
1335             if (availableTemplates.isEmpty()) {
1336                 Log.e(TAG, "No available stream templates!");
1337                 return null;
1338             }
1339 
1340             HashMap<Pair<SizeThreshold, Integer>, List<Size>> availableSizes =
1341                 enumerateAvailableSizes();
1342             if (availableSizes == null) {
1343                 Log.e(TAG, "Available size enumeration failed!");
1344                 return null;
1345             }
1346 
1347             // RAW only uses MAXIMUM size threshold
1348             Size[] rawSizes = mStreamConfigMap.getOutputSizes(ImageFormat.RAW_SENSOR);
1349             ArrayList<Size> availableRawSizes = new ArrayList<Size>();
1350             if (rawSizes != null) {
1351                 availableRawSizes.ensureCapacity(rawSizes.length);
1352                 availableRawSizes.addAll(Arrays.asList(rawSizes));
1353             }
1354 
1355             Size maxPrivateInputSize = new Size(0, 0);
1356             if (isPrivateReprocessingSupported()) {
1357                 maxPrivateInputSize = getMaxSize(mStreamConfigMap.getInputSizes(
1358                             ImageFormat.PRIVATE));
1359             }
1360 
1361             Size maxYUVInputSize = new Size(0, 0);
1362             if (isYUVReprocessingSupported()) {
1363                 maxYUVInputSize = getMaxSize(mStreamConfigMap.getInputSizes(
1364                             ImageFormat.YUV_420_888));
1365             }
1366 
1367             // Generate the available mandatory stream combinations given the supported templates
1368             // and size ranges.
1369             ArrayList<MandatoryStreamCombination> availableStreamCombinations =
1370                     new ArrayList<MandatoryStreamCombination>();
1371             availableStreamCombinations.ensureCapacity(availableTemplates.size());
1372             for (StreamCombinationTemplate combTemplate : availableTemplates) {
1373                 ArrayList<MandatoryStreamInformation> streamsInfo =
1374                         new ArrayList<MandatoryStreamInformation>();
1375                 streamsInfo.ensureCapacity(combTemplate.mStreamTemplates.length);
1376                 boolean isReprocessable = combTemplate.mReprocessType != ReprocessType.NONE;
1377                 if (isReprocessable) {
1378                     // The first and second streams in a reprocessable combination have the
1379                     // same size and format. The first is the input and the second is the output
1380                     // used for generating the subsequent input buffers.
1381                     ArrayList<Size> inputSize = new ArrayList<Size>();
1382                     int format;
1383                     if (combTemplate.mReprocessType == ReprocessType.PRIVATE) {
1384                         inputSize.add(maxPrivateInputSize);
1385                         format = ImageFormat.PRIVATE;
1386                     } else {
1387                         // Default mandatory streams only have PRIVATE / YUV reprocessing.
1388                         inputSize.add(maxYUVInputSize);
1389                         format = ImageFormat.YUV_420_888;
1390                     }
1391                     streamsInfo.add(new MandatoryStreamInformation(inputSize, format,
1392                                 /*isMaximumSize*/true, /*isInput*/true));
1393                     streamsInfo.add(new MandatoryStreamInformation(inputSize, format,
1394                             /*isMaximumSize*/true));
1395                 }
1396 
1397                 for (StreamTemplate template : combTemplate.mStreamTemplates) {
1398                     List<Size> sizes = null;
1399                     if (template.mFormat == ImageFormat.RAW_SENSOR) {
1400                         sizes = availableRawSizes;
1401                     } else {
1402                         Pair<SizeThreshold, Integer> pair;
1403                         pair = new Pair<SizeThreshold, Integer>(template.mSizeThreshold,
1404                                 new Integer(template.mFormat));
1405                         sizes = availableSizes.get(pair);
1406                     }
1407 
1408                     MandatoryStreamInformation streamInfo;
1409                     boolean isMaximumSize =
1410                             (template.mSizeThreshold == SizeThreshold.MAXIMUM);
1411                     try {
1412                         streamInfo = new MandatoryStreamInformation(sizes, template.mFormat,
1413                                 isMaximumSize);
1414                     } catch (IllegalArgumentException e) {
1415                         Log.e(TAG, "No available sizes found for format: " + template.mFormat +
1416                                 " size threshold: " + template.mSizeThreshold + " combination: " +
1417                                 combTemplate.mDescription);
1418                         return null;
1419                     }
1420                     streamsInfo.add(streamInfo);
1421                 }
1422 
1423                 MandatoryStreamCombination streamCombination;
1424                 try {
1425                     streamCombination = new MandatoryStreamCombination(streamsInfo,
1426                             combTemplate.mDescription, isReprocessable);
1427                 } catch (IllegalArgumentException e) {
1428                     Log.e(TAG, "No stream information for mandatory combination: "
1429                             + combTemplate.mDescription);
1430                     return null;
1431                 }
1432 
1433                 availableStreamCombinations.add(streamCombination);
1434             }
1435 
1436             return Collections.unmodifiableList(availableStreamCombinations);
1437         }
1438 
1439         /**
1440          * Helper method to enumerate all available sizes according to size threshold and format.
1441          */
1442         private @Nullable HashMap<Pair<SizeThreshold, Integer>, List<Size>>
enumerateAvailableSizes()1443             enumerateAvailableSizes() {
1444             final int[] formats = {
1445                 ImageFormat.PRIVATE,
1446                 ImageFormat.YUV_420_888,
1447                 ImageFormat.JPEG
1448             };
1449             Size recordingMaxSize = new Size(0, 0);
1450             Size previewMaxSize = new Size(0, 0);
1451             Size vgaSize = new Size(640, 480);
1452             // For external camera, or hidden physical camera, CamcorderProfile may not be
1453             // available, so get maximum recording size using stream configuration map.
1454             if (isExternalCamera() || mIsHiddenPhysicalCamera) {
1455                 recordingMaxSize = getMaxCameraRecordingSize();
1456             } else {
1457                 recordingMaxSize = getMaxRecordingSize();
1458             }
1459             if (recordingMaxSize == null) {
1460                 Log.e(TAG, "Failed to find maximum recording size!");
1461                 return null;
1462             }
1463 
1464             HashMap<Integer, Size[]> allSizes = new HashMap<Integer, Size[]>();
1465             for (int format : formats) {
1466                 Integer intFormat = new Integer(format);
1467                 allSizes.put(intFormat, mStreamConfigMap.getOutputSizes(format));
1468             }
1469 
1470             List<Size> previewSizes = getSizesWithinBound(
1471                     allSizes.get(new Integer(ImageFormat.PRIVATE)), kPreviewSizeBound);
1472             if ((previewSizes == null) || (previewSizes.isEmpty())) {
1473                 Log.e(TAG, "No preview sizes within preview size bound!");
1474                 return null;
1475             }
1476             List<Size> orderedPreviewSizes = getAscendingOrderSizes(previewSizes,
1477                     /*ascending*/false);
1478             previewMaxSize = getMaxPreviewSize(orderedPreviewSizes);
1479 
1480             HashMap<Pair<SizeThreshold, Integer>, List<Size>> availableSizes =
1481                     new HashMap<Pair<SizeThreshold, Integer>, List<Size>>();
1482 
1483             for (int format : formats) {
1484                 Integer intFormat = new Integer(format);
1485                 Size[] sizes = allSizes.get(intFormat);
1486                 Pair<SizeThreshold, Integer> pair = new Pair<SizeThreshold, Integer>(
1487                         SizeThreshold.VGA, intFormat);
1488                 availableSizes.put(pair, getSizesWithinBound(sizes, vgaSize));
1489 
1490                 pair = new Pair<SizeThreshold, Integer>(SizeThreshold.PREVIEW, intFormat);
1491                 availableSizes.put(pair, getSizesWithinBound(sizes, previewMaxSize));
1492 
1493                 pair = new Pair<SizeThreshold, Integer>(SizeThreshold.RECORD, intFormat);
1494                 availableSizes.put(pair, getSizesWithinBound(sizes, recordingMaxSize));
1495 
1496                 pair = new Pair<SizeThreshold, Integer>(SizeThreshold.MAXIMUM, intFormat);
1497                 availableSizes.put(pair, Arrays.asList(sizes));
1498             }
1499 
1500             return availableSizes;
1501         }
1502 
1503         /**
1504          * Compile a list of sizes smaller than or equal to given bound.
1505          * Return an empty list if there is no size smaller than or equal to the bound.
1506          */
getSizesWithinBound(@onNull Size[] sizes, @NonNull Size bound)1507         private static @Nullable List<Size> getSizesWithinBound(@NonNull Size[] sizes,
1508                 @NonNull Size bound) {
1509             ArrayList<Size> ret = new ArrayList<Size>();
1510             for (Size size : sizes) {
1511                 if (size.getWidth() <= bound.getWidth() && size.getHeight() <= bound.getHeight()) {
1512                     ret.add(size);
1513                 }
1514             }
1515 
1516             return ret;
1517         }
1518 
1519         /**
1520          * Return the lower size
1521          */
getMinSize(Size a, Size b)1522         public static @Nullable Size getMinSize(Size a, Size b) {
1523             if (a == null || b == null) {
1524                 throw new IllegalArgumentException("sizes was empty");
1525             }
1526             if (a.getWidth() * a.getHeight() < b.getHeight() * b.getWidth()) {
1527                 return a;
1528             }
1529             return b;
1530         }
1531         /**
1532          * Get the largest size by area.
1533          *
1534          * @param sizes an array of sizes, must have at least 1 element
1535          *
1536          * @return Largest Size
1537          *
1538          * @throws IllegalArgumentException if sizes was null or had 0 elements
1539          */
getMaxSize(@onNull Size... sizes)1540         public static @Nullable Size getMaxSize(@NonNull Size... sizes) {
1541             if (sizes == null || sizes.length == 0) {
1542                 throw new IllegalArgumentException("sizes was empty");
1543             }
1544 
1545             Size sz = sizes[0];
1546             for (Size size : sizes) {
1547                 if (size.getWidth() * size.getHeight() > sz.getWidth() * sz.getHeight()) {
1548                     sz = size;
1549                 }
1550             }
1551 
1552             return sz;
1553         }
1554 
1555         /**
1556          * Whether or not the hardware level reported by android.info.supportedHardwareLevel is
1557          * at least the desired one (but could be higher)
1558          */
isHardwareLevelAtLeast(int level)1559         private boolean isHardwareLevelAtLeast(int level) {
1560             final int[] sortedHwLevels = {
1561                 CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY,
1562                 CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_EXTERNAL,
1563                 CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
1564                 CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL,
1565                 CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_3
1566             };
1567             if (level == mHwLevel) {
1568                 return true;
1569             }
1570 
1571             for (int sortedlevel : sortedHwLevels) {
1572                 if (sortedlevel == level) {
1573                     return true;
1574                 } else if (sortedlevel == mHwLevel) {
1575                     return false;
1576                 }
1577             }
1578 
1579             return false;
1580         }
1581 
1582         /**
1583          * Whether or not the camera is an external camera.
1584          *
1585          * @return {@code true} if the device is external, {@code false} otherwise.
1586          */
isExternalCamera()1587         private boolean isExternalCamera() {
1588             return mHwLevel == CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_EXTERNAL;
1589         }
1590 
1591         /**
1592          * Whether or not the hardware level is at least legacy.
1593          *
1594          * @return {@code true} if the device is {@code LEGACY}, {@code false} otherwise.
1595          */
isHardwareLevelAtLeastLegacy()1596         private boolean isHardwareLevelAtLeastLegacy() {
1597             return isHardwareLevelAtLeast(CameraMetadata.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY);
1598         }
1599 
1600         /**
1601          * Whether or not the hardware level is at least limited.
1602          *
1603          * @return {@code true} if the device is {@code LIMITED} or {@code FULL},
1604          *         {@code false} otherwise (i.e. LEGACY).
1605          */
isHardwareLevelAtLeastLimited()1606         private boolean isHardwareLevelAtLeastLimited() {
1607             return isHardwareLevelAtLeast(CameraMetadata.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED);
1608         }
1609 
1610         /**
1611          * Whether or not the hardware level is at least full.
1612          *
1613          * @return {@code true} if the device is {@code FULL}, {@code false} otherwise.
1614          */
isHardwareLevelAtLeastFull()1615         private boolean isHardwareLevelAtLeastFull() {
1616             return isHardwareLevelAtLeast(CameraMetadata.INFO_SUPPORTED_HARDWARE_LEVEL_FULL);
1617         }
1618 
1619         /**
1620          * Whether or not the hardware level is at least Level 3.
1621          *
1622          * @return {@code true} if the device is {@code LEVEL3}, {@code false} otherwise.
1623          */
isHardwareLevelAtLeastLevel3()1624         private boolean isHardwareLevelAtLeastLevel3() {
1625             return isHardwareLevelAtLeast(CameraMetadata.INFO_SUPPORTED_HARDWARE_LEVEL_3);
1626         }
1627 
1628         /**
1629          * Determine whether the current device supports a capability or not.
1630          *
1631          * @param capability (non-negative)
1632          *
1633          * @return {@code true} if the capability is supported, {@code false} otherwise.
1634          *
1635          */
isCapabilitySupported(int capability)1636         private boolean isCapabilitySupported(int capability) {
1637             return mCapabilities.contains(capability);
1638         }
1639 
1640         /**
1641          * Check whether the current device is backward compatible.
1642          */
isColorOutputSupported()1643         private boolean isColorOutputSupported() {
1644             return isCapabilitySupported(
1645                     CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE);
1646         }
1647 
1648         /**
1649          * Check whether the current device supports private reprocessing.
1650          */
isPrivateReprocessingSupported()1651         private boolean isPrivateReprocessingSupported() {
1652             return isCapabilitySupported(
1653                     CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_PRIVATE_REPROCESSING);
1654         }
1655 
1656         /**
1657          * Check whether the current device supports YUV reprocessing.
1658          */
isYUVReprocessingSupported()1659         private boolean isYUVReprocessingSupported() {
1660             return isCapabilitySupported(
1661                     CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_YUV_REPROCESSING);
1662         }
1663 
1664         /**
1665          * Check whether the current device supports YUV reprocessing.
1666          */
isRemosaicReprocessingSupported()1667         private boolean isRemosaicReprocessingSupported() {
1668             return isCapabilitySupported(
1669                     CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_REMOSAIC_REPROCESSING);
1670         }
1671 
1672         /**
1673          * Return the maximum supported video size using the camcorder profile information.
1674          *
1675          * @return Maximum supported video size.
1676          */
getMaxRecordingSize()1677         private @Nullable Size getMaxRecordingSize() {
1678             int quality =
1679                     CamcorderProfile.hasProfile(mCameraId, CamcorderProfile.QUALITY_2160P) ?
1680                         CamcorderProfile.QUALITY_2160P :
1681                     CamcorderProfile.hasProfile(mCameraId, CamcorderProfile.QUALITY_1080P) ?
1682                         CamcorderProfile.QUALITY_1080P :
1683                     CamcorderProfile.hasProfile(mCameraId, CamcorderProfile.QUALITY_720P) ?
1684                         CamcorderProfile.QUALITY_720P :
1685                     CamcorderProfile.hasProfile(mCameraId, CamcorderProfile.QUALITY_480P) ?
1686                         CamcorderProfile.QUALITY_480P :
1687                     CamcorderProfile.hasProfile(mCameraId, CamcorderProfile.QUALITY_QVGA) ?
1688                         CamcorderProfile.QUALITY_QVGA :
1689                     CamcorderProfile.hasProfile(mCameraId, CamcorderProfile.QUALITY_CIF) ?
1690                         CamcorderProfile.QUALITY_CIF :
1691                     CamcorderProfile.hasProfile(mCameraId, CamcorderProfile.QUALITY_QCIF) ?
1692                         CamcorderProfile.QUALITY_QCIF :
1693                         -1;
1694 
1695             if (quality < 0) {
1696                 return null;
1697             }
1698 
1699             CamcorderProfile maxProfile = CamcorderProfile.get(mCameraId, quality);
1700             return new Size(maxProfile.videoFrameWidth, maxProfile.videoFrameHeight);
1701         }
1702 
1703         /**
1704          * Return the maximum supported video size for cameras using data from
1705          * the stream configuration map.
1706          *
1707          * @return Maximum supported video size.
1708          */
getMaxCameraRecordingSize()1709         private @NonNull Size getMaxCameraRecordingSize() {
1710             final Size FULLHD = new Size(1920, 1080);
1711 
1712             Size[] videoSizeArr = mStreamConfigMap.getOutputSizes(
1713                     android.media.MediaRecorder.class);
1714             List<Size> sizes = new ArrayList<Size>();
1715             for (Size sz: videoSizeArr) {
1716                 if (sz.getWidth() <= FULLHD.getWidth() && sz.getHeight() <= FULLHD.getHeight()) {
1717                     sizes.add(sz);
1718                 }
1719             }
1720             List<Size> videoSizes = getAscendingOrderSizes(sizes, /*ascending*/false);
1721             for (Size sz : videoSizes) {
1722                 long minFrameDuration = mStreamConfigMap.getOutputMinFrameDuration(
1723                         android.media.MediaRecorder.class, sz);
1724                 // Give some margin for rounding error
1725                 if (minFrameDuration > (1e9 / 30.1)) {
1726                     Log.i(TAG, "External camera " + mCameraId + " has max video size:" + sz);
1727                     return sz;
1728                 }
1729             }
1730             Log.w(TAG, "Camera " + mCameraId + " does not support any 30fps video output");
1731             return FULLHD; // doesn't matter what size is returned here
1732         }
1733 
getMaxPreviewSize(List<Size> orderedPreviewSizes)1734         private @NonNull Size getMaxPreviewSize(List<Size> orderedPreviewSizes) {
1735             if (orderedPreviewSizes != null) {
1736                 for (Size size : orderedPreviewSizes) {
1737                     if ((mDisplaySize.getWidth() >= size.getWidth()) &&
1738                             (mDisplaySize.getHeight() >= size.getHeight())) {
1739                         return size;
1740                     }
1741                 }
1742             }
1743 
1744             Log.w(TAG,"Camera " + mCameraId + " maximum preview size search failed with "
1745                     + "display size " + mDisplaySize);
1746             return kPreviewSizeBound;
1747         }
1748 
1749         /**
1750          * Size comparator that compares the number of pixels it covers.
1751          *
1752          * <p>If two the areas of two sizes are same, compare the widths.</p>
1753          */
1754         public static class SizeComparator implements Comparator<Size> {
1755             @Override
compare(@onNull Size lhs, @NonNull Size rhs)1756             public int compare(@NonNull Size lhs, @NonNull Size rhs) {
1757                 return StreamConfigurationMap.compareSizes(lhs.getWidth(), lhs.getHeight(),
1758                         rhs.getWidth(), rhs.getHeight());
1759             }
1760         }
1761 
1762         /**
1763          * Get a sorted list of sizes from a given size list.
1764          *
1765          * <p>
1766          * The size is compare by area it covers, if the areas are same, then
1767          * compare the widths.
1768          * </p>
1769          *
1770          * @param sizeList The input size list to be sorted
1771          * @param ascending True if the order is ascending, otherwise descending order
1772          * @return The ordered list of sizes
1773          */
getAscendingOrderSizes( @onNull final List<Size> sizeList, boolean ascending)1774         private static @NonNull List<Size> getAscendingOrderSizes(
1775                 @NonNull final List<Size> sizeList, boolean ascending) {
1776             Comparator<Size> comparator = new SizeComparator();
1777             List<Size> sortedSizes = new ArrayList<Size>();
1778             sortedSizes.addAll(sizeList);
1779             Collections.sort(sortedSizes, comparator);
1780             if (!ascending) {
1781                 Collections.reverse(sortedSizes);
1782             }
1783 
1784             return sortedSizes;
1785         }
1786     }
1787 }
1788