blob: d44b6b5aeb29865b3cfaf581b5721b01c8924fa4 [file] [log] [blame]
Emilian Peev423cbd72018-11-10 18:37:45 +00001/*
2 * Copyright (C) 2018 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.hardware.camera2.params;
18
19import static com.android.internal.util.Preconditions.*;
20import static android.hardware.camera2.params.StreamConfigurationMap.checkArgumentFormat;
21
Emilian Peev0081bcf2019-02-26 14:55:58 -080022import android.annotation.IntRange;
Emilian Peev423cbd72018-11-10 18:37:45 +000023import android.annotation.NonNull;
Emilian Peev0081bcf2019-02-26 14:55:58 -080024import android.annotation.Nullable;
Emilian Peev423cbd72018-11-10 18:37:45 +000025import android.content.Context;
26import android.graphics.ImageFormat;
Emilian Peev0081bcf2019-02-26 14:55:58 -080027import android.graphics.ImageFormat.Format;
Emilian Peev423cbd72018-11-10 18:37:45 +000028import android.hardware.camera2.CameraCharacteristics;
29import android.hardware.camera2.CameraCharacteristics.Key;
30import android.hardware.camera2.CameraDevice;
31import android.hardware.camera2.CameraMetadata;
32import android.hardware.camera2.params.StreamConfigurationMap;
33import android.hardware.camera2.utils.HashCodeHelpers;
34import android.graphics.PixelFormat;
35import android.media.CamcorderProfile;
36import android.util.Size;
37import android.util.Log;
38import android.util.Pair;
39
40import java.util.Arrays;
41import java.util.ArrayList;
42import java.util.Collections;
43import java.util.Comparator;
44import java.util.HashMap;
45import java.util.List;
46
47/**
48 * Immutable class to store the available mandatory stream combination.
49 *
Emilian Peev0081bcf2019-02-26 14:55:58 -080050 * <p>A mandatory stream combination refers to a specific entry in the documented sets of
51 * required stream {@link CameraDevice#createCaptureSession combinations}.
52 * These combinations of streams are required to be supported by the camera device.
Emilian Peev423cbd72018-11-10 18:37:45 +000053 *
54 * <p>The list of stream combinations is available by invoking
55 * {@link CameraCharacteristics#get} and passing key
56 * {@link android.hardware.camera2.CameraCharacteristics#SCALER_MANDATORY_STREAM_COMBINATIONS}.</p>
57 */
58public final class MandatoryStreamCombination {
59 private static final String TAG = "MandatoryStreamCombination";
60 /**
61 * Immutable class to store available mandatory stream information.
62 */
63 public static final class MandatoryStreamInformation {
64 private final int mFormat;
65 private final ArrayList<Size> mAvailableSizes = new ArrayList<Size> ();
Emilian Peev275da0b2018-11-15 18:20:24 +000066 private final boolean mIsInput;
Emilian Peev423cbd72018-11-10 18:37:45 +000067
68 /**
69 * Create a new {@link MandatoryStreamInformation}.
70 *
Emilian Peev275da0b2018-11-15 18:20:24 +000071 @param availableSizes List of possible stream sizes.
Emilian Peev423cbd72018-11-10 18:37:45 +000072 * @param format Image format.
73 *
74 * @throws IllegalArgumentException
75 * if sizes is empty or if the format was not user-defined in
76 * ImageFormat/PixelFormat.
77 * @hide
78 */
79 public MandatoryStreamInformation(@NonNull List<Size> availableSizes, int format) {
Emilian Peev275da0b2018-11-15 18:20:24 +000080 this(availableSizes, format, /*isInput*/false);
81 }
82
83 /**
84 * Create a new {@link MandatoryStreamInformation}.
85 *
86 @param availableSizes List of possible stream sizes.
87 * @param format Image format.
88 * @param isInput Flag indicating whether this stream is input.
89 *
90 * @throws IllegalArgumentException
91 * if sizes is empty or if the format was not user-defined in
92 * ImageFormat/PixelFormat.
93 * @hide
94 */
Emilian Peev0081bcf2019-02-26 14:55:58 -080095 public MandatoryStreamInformation(@NonNull List<Size> availableSizes, @Format int format,
Emilian Peev275da0b2018-11-15 18:20:24 +000096 boolean isInput) {
Emilian Peev423cbd72018-11-10 18:37:45 +000097 if (availableSizes.isEmpty()) {
98 throw new IllegalArgumentException("No available sizes");
99 }
100 mAvailableSizes.addAll(availableSizes);
101 mFormat = checkArgumentFormat(format);
Emilian Peev275da0b2018-11-15 18:20:24 +0000102 mIsInput = isInput;
103 }
104
105 /**
106 * Confirms whether or not this is an input stream.
107 * @return true in case the stream is input, false otherwise.
108 */
109 public boolean isInput() {
110 return mIsInput;
Emilian Peev423cbd72018-11-10 18:37:45 +0000111 }
112
113 /**
114 * Return the list of available sizes for this mandatory stream.
115 *
116 * <p>Per documented {@link CameraDevice#createCaptureSession guideline} the largest
117 * resolution in the result will be tested and guaranteed to work. If clients want to use
118 * smaller sizes, then the resulting
119 * {@link android.hardware.camera2.params.SessionConfiguration session configuration} can
120 * be tested either by calling {@link CameraDevice#createCaptureSession} or
121 * {@link CameraDevice#isSessionConfigurationSupported}.
122 *
123 * @return non-modifiable ascending list of available sizes.
124 */
Emilian Peev0081bcf2019-02-26 14:55:58 -0800125 public @NonNull List<Size> getAvailableSizes() {
Emilian Peev423cbd72018-11-10 18:37:45 +0000126 return Collections.unmodifiableList(mAvailableSizes);
127 }
128
129 /**
130 * Retrieve the mandatory stream {@code format}.
131 *
132 * @return integer format.
133 */
Emilian Peev0081bcf2019-02-26 14:55:58 -0800134 public @Format int getFormat() {
Emilian Peev423cbd72018-11-10 18:37:45 +0000135 return mFormat;
136 }
137
138 /**
139 * Check if this {@link MandatoryStreamInformation} is equal to another
140 * {@link MandatoryStreamInformation}.
141 *
142 * <p>Two vectors are only equal if and only if each of the respective elements is
143 * equal.</p>
144 *
145 * @return {@code true} if the objects were equal, {@code false} otherwise
146 */
147 @Override
148 public boolean equals(final Object obj) {
149 if (obj == null) {
150 return false;
151 }
152 if (this == obj) {
153 return true;
154 }
155 if (obj instanceof MandatoryStreamInformation) {
156 final MandatoryStreamInformation other = (MandatoryStreamInformation) obj;
Emilian Peev275da0b2018-11-15 18:20:24 +0000157 if ((mFormat != other.mFormat) || (mIsInput != other.mIsInput) ||
Emilian Peev423cbd72018-11-10 18:37:45 +0000158 (mAvailableSizes.size() != other.mAvailableSizes.size())) {
159 return false;
160 }
161
162 return mAvailableSizes.equals(other.mAvailableSizes);
163 }
164
165 return false;
166 }
167
168 /**
169 * {@inheritDoc}
170 */
171 @Override
172 public int hashCode() {
Emilian Peev275da0b2018-11-15 18:20:24 +0000173 return HashCodeHelpers.hashCode(mFormat, Boolean.hashCode(mIsInput),
174 mAvailableSizes.hashCode());
Emilian Peev423cbd72018-11-10 18:37:45 +0000175 }
176 }
177
Emilian Peev275da0b2018-11-15 18:20:24 +0000178 private final String mDescription;
179 private final boolean mIsReprocessable;
Emilian Peev423cbd72018-11-10 18:37:45 +0000180 private final ArrayList<MandatoryStreamInformation> mStreamsInformation =
181 new ArrayList<MandatoryStreamInformation>();
182 /**
183 * Create a new {@link MandatoryStreamCombination}.
184 *
185 * @param streamsInformation list of available streams in the stream combination.
186 * @param description Summary of the stream combination use case.
Emilian Peev275da0b2018-11-15 18:20:24 +0000187 * @param isReprocessable Flag whether the mandatory stream combination is reprocessable.
Emilian Peev423cbd72018-11-10 18:37:45 +0000188 *
189 * @throws IllegalArgumentException
190 * if stream information is empty
191 * @hide
192 */
193 public MandatoryStreamCombination(@NonNull List<MandatoryStreamInformation> streamsInformation,
Emilian Peev0081bcf2019-02-26 14:55:58 -0800194 @NonNull String description, boolean isReprocessable) {
Emilian Peev423cbd72018-11-10 18:37:45 +0000195 if (streamsInformation.isEmpty()) {
196 throw new IllegalArgumentException("Empty stream information");
197 }
198 mStreamsInformation.addAll(streamsInformation);
Emilian Peev275da0b2018-11-15 18:20:24 +0000199 mDescription = description;
200 mIsReprocessable = isReprocessable;
Emilian Peev423cbd72018-11-10 18:37:45 +0000201 }
202
203 /**
204 * Get the mandatory stream combination description.
205 *
Emilian Peev0081bcf2019-02-26 14:55:58 -0800206 * @return CharSequence with the mandatory combination description.
Emilian Peev423cbd72018-11-10 18:37:45 +0000207 */
Emilian Peev0081bcf2019-02-26 14:55:58 -0800208 public @NonNull CharSequence getDescription() {
Emilian Peev275da0b2018-11-15 18:20:24 +0000209 return mDescription;
210 }
211
212 /**
Emilian Peev0081bcf2019-02-26 14:55:58 -0800213 * Indicates whether the mandatory stream combination is reprocessable. Reprocessable is defined
214 * as a stream combination that contains one input stream
215 * ({@link MandatoryStreamInformation#isInput} return true).
Emilian Peev275da0b2018-11-15 18:20:24 +0000216 *
217 * @return {@code true} in case the mandatory stream combination contains an input,
218 * {@code false} otherwise.
219 */
220 public boolean isReprocessable() {
221 return mIsReprocessable;
Emilian Peev423cbd72018-11-10 18:37:45 +0000222 }
223
224 /**
225 * Get information about each stream in the mandatory combination.
226 *
227 * @return Non-modifiable list of stream information.
228 *
229 */
Emilian Peev0081bcf2019-02-26 14:55:58 -0800230 public @NonNull List<MandatoryStreamInformation> getStreamsInformation() {
Emilian Peev423cbd72018-11-10 18:37:45 +0000231 return Collections.unmodifiableList(mStreamsInformation);
232 }
233
234 /**
235 * Check if this {@link MandatoryStreamCombination} is equal to another
236 * {@link MandatoryStreamCombination}.
237 *
238 * <p>Two vectors are only equal if and only if each of the respective elements is equal.</p>
239 *
240 * @return {@code true} if the objects were equal, {@code false} otherwise
241 */
242 @Override
243 public boolean equals(final Object obj) {
244 if (obj == null) {
245 return false;
246 }
247 if (this == obj) {
248 return true;
249 }
250 if (obj instanceof MandatoryStreamCombination) {
251 final MandatoryStreamCombination other = (MandatoryStreamCombination) obj;
Emilian Peev275da0b2018-11-15 18:20:24 +0000252 if ((mDescription != other.mDescription) ||
253 (mIsReprocessable != other.mIsReprocessable) ||
Emilian Peev423cbd72018-11-10 18:37:45 +0000254 (mStreamsInformation.size() != other.mStreamsInformation.size())) {
255 return false;
256 }
257
Emilian Peev275da0b2018-11-15 18:20:24 +0000258 return mStreamsInformation.equals(other.mStreamsInformation);
Emilian Peev423cbd72018-11-10 18:37:45 +0000259 }
260
261 return false;
262 }
263
264 /**
265 * {@inheritDoc}
266 */
267 @Override
268 public int hashCode() {
Emilian Peev275da0b2018-11-15 18:20:24 +0000269 return HashCodeHelpers.hashCode(Boolean.hashCode(mIsReprocessable), mDescription.hashCode(),
270 mStreamsInformation.hashCode());
Emilian Peev423cbd72018-11-10 18:37:45 +0000271 }
272
273 private static enum SizeThreshold { VGA, PREVIEW, RECORD, MAXIMUM }
Emilian Peev275da0b2018-11-15 18:20:24 +0000274 private static enum ReprocessType { NONE, PRIVATE, YUV }
Emilian Peev423cbd72018-11-10 18:37:45 +0000275 private static final class StreamTemplate {
276 public int mFormat;
277 public SizeThreshold mSizeThreshold;
Emilian Peev275da0b2018-11-15 18:20:24 +0000278 public boolean mIsInput;
Emilian Peev423cbd72018-11-10 18:37:45 +0000279 public StreamTemplate(int format, SizeThreshold sizeThreshold) {
Emilian Peev275da0b2018-11-15 18:20:24 +0000280 this(format, sizeThreshold, /*isInput*/false);
281 }
282
Emilian Peev0081bcf2019-02-26 14:55:58 -0800283 public StreamTemplate(@Format int format, @NonNull SizeThreshold sizeThreshold,
284 boolean isInput) {
Emilian Peev423cbd72018-11-10 18:37:45 +0000285 mFormat = format;
286 mSizeThreshold = sizeThreshold;
Emilian Peev275da0b2018-11-15 18:20:24 +0000287 mIsInput = isInput;
Emilian Peev423cbd72018-11-10 18:37:45 +0000288 }
289 }
290
291 private static final class StreamCombinationTemplate {
292 public StreamTemplate[] mStreamTemplates;
293 public String mDescription;
Emilian Peev275da0b2018-11-15 18:20:24 +0000294 public ReprocessType mReprocessType;
Emilian Peev423cbd72018-11-10 18:37:45 +0000295
Emilian Peev0081bcf2019-02-26 14:55:58 -0800296 public StreamCombinationTemplate(@NonNull StreamTemplate[] streamTemplates,
297 @NonNull String description) {
Emilian Peev275da0b2018-11-15 18:20:24 +0000298 this(streamTemplates, description, /*reprocessType*/ReprocessType.NONE);
299 }
300
Emilian Peev0081bcf2019-02-26 14:55:58 -0800301 public StreamCombinationTemplate(@NonNull StreamTemplate[] streamTemplates,
302 @NonNull String description,
Emilian Peev275da0b2018-11-15 18:20:24 +0000303 ReprocessType reprocessType) {
Emilian Peev423cbd72018-11-10 18:37:45 +0000304 mStreamTemplates = streamTemplates;
Emilian Peev275da0b2018-11-15 18:20:24 +0000305 mReprocessType = reprocessType;
Emilian Peev423cbd72018-11-10 18:37:45 +0000306 mDescription = description;
307 }
308 }
309
310 private static StreamCombinationTemplate sLegacyCombinations[] = {
311 new StreamCombinationTemplate(new StreamTemplate [] {
312 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.MAXIMUM) },
313 "Simple preview, GPU video processing, or no-preview video recording"),
314 new StreamCombinationTemplate(new StreamTemplate [] {
315 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM) },
316 "No-viewfinder still image capture"),
317 new StreamCombinationTemplate(new StreamTemplate [] {
318 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.MAXIMUM) },
319 "In-application video/image processing"),
320 new StreamCombinationTemplate(new StreamTemplate [] {
321 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
322 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM) },
323 "Standard still imaging"),
324 new StreamCombinationTemplate(new StreamTemplate [] {
325 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
326 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM) },
327 "In-app processing plus still capture"),
328 new StreamCombinationTemplate(new StreamTemplate [] {
329 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
330 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW) },
331 "Standard recording"),
332 new StreamCombinationTemplate(new StreamTemplate [] {
333 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
334 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW) },
335 "Preview plus in-app processing"),
336 new StreamCombinationTemplate(new StreamTemplate [] {
337 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
338 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
339 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM) },
340 "Still capture plus in-app processing")
341 };
342
343 private static StreamCombinationTemplate sLimitedCombinations[] = {
344 new StreamCombinationTemplate(new StreamTemplate [] {
345 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
346 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.RECORD)},
347 "High-resolution video recording with preview"),
348 new StreamCombinationTemplate(new StreamTemplate [] {
349 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
350 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.RECORD)},
351 "High-resolution in-app video processing with preview"),
352 new StreamCombinationTemplate(new StreamTemplate [] {
353 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
354 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.RECORD) },
355 "Two-input in-app video processing"),
356 new StreamCombinationTemplate(new StreamTemplate [] {
357 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
358 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.RECORD),
359 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.RECORD) },
360 "High-resolution recording with video snapshot"),
361 new StreamCombinationTemplate(new StreamTemplate [] {
362 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
363 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.RECORD),
364 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.RECORD) },
365 "High-resolution in-app processing with video snapshot"),
366 new StreamCombinationTemplate(new StreamTemplate [] {
367 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
368 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
369 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM) },
370 "Two-input in-app processing with still capture")
371 };
372
373 private static StreamCombinationTemplate sBurstCombinations[] = {
374 new StreamCombinationTemplate(new StreamTemplate [] {
375 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
376 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.MAXIMUM) },
377 "Maximum-resolution GPU processing with preview"),
378 new StreamCombinationTemplate(new StreamTemplate [] {
379 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
380 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.MAXIMUM) },
381 "Maximum-resolution in-app processing with preview"),
382 new StreamCombinationTemplate(new StreamTemplate [] {
383 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
384 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.MAXIMUM) },
385 "Maximum-resolution two-input in-app processsing")
386 };
387
388 private static StreamCombinationTemplate sFullCombinations[] = {
389 new StreamCombinationTemplate(new StreamTemplate [] {
390 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.MAXIMUM),
391 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.MAXIMUM) },
392 "Maximum-resolution GPU processing with preview"),
393 new StreamCombinationTemplate(new StreamTemplate [] {
394 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.MAXIMUM),
395 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.MAXIMUM) },
396 "Maximum-resolution in-app processing with preview"),
397 new StreamCombinationTemplate(new StreamTemplate [] {
398 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.MAXIMUM),
399 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.MAXIMUM) },
400 "Maximum-resolution two-input in-app processsing"),
401 new StreamCombinationTemplate(new StreamTemplate [] {
402 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
403 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
404 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM) },
405 "Video recording with maximum-size video snapshot"),
406 new StreamCombinationTemplate(new StreamTemplate [] {
407 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.VGA),
408 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
409 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.MAXIMUM) },
410 "Standard video recording plus maximum-resolution in-app processing"),
411 new StreamCombinationTemplate(new StreamTemplate [] {
412 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.VGA),
413 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
414 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.MAXIMUM) },
415 "Preview plus two-input maximum-resolution in-app processing")
416 };
417
418 private static StreamCombinationTemplate sRawCombinations[] = {
419 new StreamCombinationTemplate(new StreamTemplate [] {
420 new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.MAXIMUM) },
421 "No-preview DNG capture"),
422 new StreamCombinationTemplate(new StreamTemplate [] {
423 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
424 new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.MAXIMUM) },
425 "Standard DNG capture"),
426 new StreamCombinationTemplate(new StreamTemplate [] {
427 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
428 new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.MAXIMUM) },
429 "In-app processing plus DNG capture"),
430 new StreamCombinationTemplate(new StreamTemplate [] {
431 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
432 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
433 new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.MAXIMUM) },
434 "Video recording with DNG capture"),
435 new StreamCombinationTemplate(new StreamTemplate [] {
436 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
437 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
438 new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.MAXIMUM) },
439 "Preview with in-app processing and DNG capture"),
440 new StreamCombinationTemplate(new StreamTemplate [] {
441 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
442 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
443 new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.MAXIMUM) },
444 "Two-input in-app processing plus DNG capture"),
445 new StreamCombinationTemplate(new StreamTemplate [] {
446 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
447 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM),
448 new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.MAXIMUM) },
449 "Still capture with simultaneous JPEG and DNG"),
450 new StreamCombinationTemplate(new StreamTemplate [] {
451 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
452 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM),
453 new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.MAXIMUM) },
454 "In-app processing with simultaneous JPEG and DNG")
455 };
456
457 private static StreamCombinationTemplate sLevel3Combinations[] = {
458 new StreamCombinationTemplate(new StreamTemplate [] {
459 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
460 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.VGA),
461 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.MAXIMUM),
462 new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.MAXIMUM) },
463 "In-app viewfinder analysis with dynamic selection of output format"),
464 new StreamCombinationTemplate(new StreamTemplate [] {
465 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
466 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.VGA),
467 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM),
468 new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.MAXIMUM) },
469 "In-app viewfinder analysis with dynamic selection of output format")
470 };
471
Emilian Peev275da0b2018-11-15 18:20:24 +0000472 private static StreamCombinationTemplate sLimitedPrivateReprocCombinations[] = {
473 new StreamCombinationTemplate(new StreamTemplate [] {
474 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM) },
475 "No-viewfinder still image reprocessing",
476 /*reprocessType*/ ReprocessType.PRIVATE),
477 new StreamCombinationTemplate(new StreamTemplate [] {
478 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
479 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM) },
480 "ZSL(Zero-Shutter-Lag) still imaging",
481 /*reprocessType*/ ReprocessType.PRIVATE),
482 new StreamCombinationTemplate(new StreamTemplate [] {
483 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
484 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM) },
485 "ZSL still and in-app processing imaging",
486 /*reprocessType*/ ReprocessType.PRIVATE),
487 new StreamCombinationTemplate(new StreamTemplate [] {
488 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
489 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
490 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM) },
491 "ZSL in-app processing with still capture",
492 /*reprocessType*/ ReprocessType.PRIVATE),
493 };
494
495 private static StreamCombinationTemplate sLimitedYUVReprocCombinations[] = {
496 new StreamCombinationTemplate(new StreamTemplate [] {
497 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM) },
498 "No-viewfinder still image reprocessing",
499 /*reprocessType*/ ReprocessType.YUV),
500 new StreamCombinationTemplate(new StreamTemplate [] {
501 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
502 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM) },
503 "ZSL(Zero-Shutter-Lag) still imaging",
504 /*reprocessType*/ ReprocessType.YUV),
505 new StreamCombinationTemplate(new StreamTemplate [] {
506 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
507 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM) },
508 "ZSL still and in-app processing imaging",
509 /*reprocessType*/ ReprocessType.YUV),
510 new StreamCombinationTemplate(new StreamTemplate [] {
511 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
512 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
513 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM) },
514 "ZSL in-app processing with still capture",
515 /*reprocessType*/ ReprocessType.YUV),
516 };
517
518 private static StreamCombinationTemplate sFullPrivateReprocCombinations[] = {
519 new StreamCombinationTemplate(new StreamTemplate [] {
520 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
521 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.RECORD) },
522 "High-resolution ZSL in-app video processing with regular preview",
523 /*reprocessType*/ ReprocessType.PRIVATE),
524 new StreamCombinationTemplate(new StreamTemplate [] {
525 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
526 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.MAXIMUM) },
527 "Maximum-resolution ZSL in-app processing with regular preview",
528 /*reprocessType*/ ReprocessType.PRIVATE),
529 new StreamCombinationTemplate(new StreamTemplate [] {
530 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
531 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.MAXIMUM) },
532 "Maximum-resolution two-input ZSL in-app processing",
533 /*reprocessType*/ ReprocessType.PRIVATE),
534 new StreamCombinationTemplate(new StreamTemplate [] {
535 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
536 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
537 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM) },
538 "ZSL still capture and in-app processing",
539 /*reprocessType*/ ReprocessType.PRIVATE),
540 };
541
542 private static StreamCombinationTemplate sFullYUVReprocCombinations[] = {
543 new StreamCombinationTemplate(new StreamTemplate [] {
544 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW) },
545 "Maximum-resolution multi-frame image fusion in-app processing with regular "
546 + "preview",
547 /*reprocessType*/ ReprocessType.YUV),
548 new StreamCombinationTemplate(new StreamTemplate [] {
549 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW) },
550 "Maximum-resolution multi-frame image fusion two-input in-app processing",
551 /*reprocessType*/ ReprocessType.YUV),
552 new StreamCombinationTemplate(new StreamTemplate [] {
553 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
554 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.RECORD) },
555 "High-resolution ZSL in-app video processing with regular preview",
556 /*reprocessType*/ ReprocessType.YUV),
557 new StreamCombinationTemplate(new StreamTemplate [] {
558 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
559 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
560 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM) },
561 "ZSL still capture and in-app processing",
562 /*reprocessType*/ ReprocessType.YUV),
563 };
564
565 private static StreamCombinationTemplate sRAWPrivateReprocCombinations[] = {
566 new StreamCombinationTemplate(new StreamTemplate [] {
567 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
568 new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.MAXIMUM) },
569 "Mutually exclusive ZSL in-app processing and DNG capture",
570 /*reprocessType*/ ReprocessType.PRIVATE),
571 new StreamCombinationTemplate(new StreamTemplate [] {
572 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
573 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
574 new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.MAXIMUM) },
575 "Mutually exclusive ZSL in-app processing and preview with DNG capture",
576 /*reprocessType*/ ReprocessType.PRIVATE),
577 new StreamCombinationTemplate(new StreamTemplate [] {
578 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
579 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
580 new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.MAXIMUM) },
581 "Mutually exclusive ZSL two-input in-app processing and DNG capture",
582 /*reprocessType*/ ReprocessType.PRIVATE),
583 new StreamCombinationTemplate(new StreamTemplate [] {
584 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
585 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM),
586 new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.MAXIMUM) },
587 "Mutually exclusive ZSL still capture and preview with DNG capture",
588 /*reprocessType*/ ReprocessType.PRIVATE),
589 new StreamCombinationTemplate(new StreamTemplate [] {
590 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
591 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM),
592 new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.MAXIMUM) },
593 "Mutually exclusive ZSL in-app processing with still capture and DNG capture",
594 /*reprocessType*/ ReprocessType.PRIVATE),
595 };
596
597 private static StreamCombinationTemplate sRAWYUVReprocCombinations[] = {
598 new StreamCombinationTemplate(new StreamTemplate [] {
599 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
600 new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.MAXIMUM) },
601 "Mutually exclusive ZSL in-app processing and DNG capture",
602 /*reprocessType*/ ReprocessType.YUV),
603 new StreamCombinationTemplate(new StreamTemplate [] {
604 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
605 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
606 new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.MAXIMUM) },
607 "Mutually exclusive ZSL in-app processing and preview with DNG capture",
608 /*reprocessType*/ ReprocessType.YUV),
609 new StreamCombinationTemplate(new StreamTemplate [] {
610 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
611 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
612 new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.MAXIMUM) },
613 "Mutually exclusive ZSL two-input in-app processing and DNG capture",
614 /*reprocessType*/ ReprocessType.YUV),
615 new StreamCombinationTemplate(new StreamTemplate [] {
616 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
617 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM),
618 new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.MAXIMUM) },
619 "Mutually exclusive ZSL still capture and preview with DNG capture",
620 /*reprocessType*/ ReprocessType.YUV),
621 new StreamCombinationTemplate(new StreamTemplate [] {
622 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
623 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM),
624 new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.MAXIMUM) },
625 "Mutually exclusive ZSL in-app processing with still capture and DNG capture",
626 /*reprocessType*/ ReprocessType.YUV),
627 };
628
629 private static StreamCombinationTemplate sLevel3PrivateReprocCombinations[] = {
630 new StreamCombinationTemplate(new StreamTemplate [] {
631 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
632 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.VGA),
633 new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.MAXIMUM),
634 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM) },
635 "In-app viewfinder analysis with ZSL, RAW, and JPEG reprocessing output",
636 /*reprocessType*/ ReprocessType.PRIVATE),
637 };
638
639 private static StreamCombinationTemplate sLevel3YUVReprocCombinations[] = {
640 new StreamCombinationTemplate(new StreamTemplate [] {
641 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
642 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.VGA),
643 new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.MAXIMUM) },
644 "In-app viewfinder analysis with ZSL and RAW",
645 /*reprocessType*/ ReprocessType.YUV),
646 new StreamCombinationTemplate(new StreamTemplate [] {
647 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
648 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.VGA),
649 new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.MAXIMUM),
650 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM) },
651 "In-app viewfinder analysis with ZSL, RAW, and JPEG reprocessing output",
652 /*reprocessType*/ ReprocessType.YUV),
653 };
654
Emilian Peev423cbd72018-11-10 18:37:45 +0000655 /**
656 * Helper builder class to generate a list of available mandatory stream combinations.
657 * @hide
658 */
659 public static final class Builder {
660 private Size mDisplaySize;
661 private List<Integer> mCapabilities;
662 private int mHwLevel, mCameraId;
663 private StreamConfigurationMap mStreamConfigMap;
664
665 private final Size kPreviewSizeBound = new Size(1920, 1088);
666
667 /**
668 * Helper class to be used to generate the available mandatory stream combinations.
669 *
670 * @param cameraId Current camera id.
671 * @param hwLevel The camera HW level as reported by android.info.supportedHardwareLevel.
672 * @param displaySize The device display size.
673 * @param capabilities The camera device capabilities.
674 * @param sm The camera device stream configuration map.
675 */
676 public Builder(int cameraId, int hwLevel, @NonNull Size displaySize,
677 @NonNull List<Integer> capabilities, @NonNull StreamConfigurationMap sm) {
678 mCameraId = cameraId;
679 mDisplaySize = displaySize;
680 mCapabilities = capabilities;
681 mStreamConfigMap = sm;
682 mHwLevel = hwLevel;
683 }
684
685 /**
686 * Retrieve a list of all available mandatory stream combinations.
687 *
688 * @return a non-modifiable list of supported mandatory stream combinations or
689 * null in case device is not backward compatible or the method encounters
690 * an error.
691 */
Emilian Peev0081bcf2019-02-26 14:55:58 -0800692 public @Nullable List<MandatoryStreamCombination>
693 getAvailableMandatoryStreamCombinations() {
Emilian Peev423cbd72018-11-10 18:37:45 +0000694 if (!isColorOutputSupported()) {
695 Log.v(TAG, "Device is not backward compatible!");
696 return null;
697 }
698
699 if ((mCameraId < 0) && !isExternalCamera()) {
700 Log.i(TAG, "Invalid camera id");
701 return null;
702 }
703
704 ArrayList<StreamCombinationTemplate> availableTemplates =
705 new ArrayList<StreamCombinationTemplate> ();
706 if (isHardwareLevelAtLeastLegacy()) {
707 availableTemplates.addAll(Arrays.asList(sLegacyCombinations));
708 }
709
710 // External devices are identical to limited devices w.r.t. stream combinations.
711 if (isHardwareLevelAtLeastLimited() || isExternalCamera()) {
712 availableTemplates.addAll(Arrays.asList(sLimitedCombinations));
Emilian Peev275da0b2018-11-15 18:20:24 +0000713
714 if (isPrivateReprocessingSupported()) {
715 availableTemplates.addAll(Arrays.asList(sLimitedPrivateReprocCombinations));
716 }
717
718 if (isYUVReprocessingSupported()) {
719 availableTemplates.addAll(Arrays.asList(sLimitedYUVReprocCombinations));
720 }
721
Emilian Peev423cbd72018-11-10 18:37:45 +0000722 }
723
724 if (isCapabilitySupported(
725 CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE)) {
726 availableTemplates.addAll(Arrays.asList(sBurstCombinations));
727 }
728
729 if (isHardwareLevelAtLeastFull()) {
730 availableTemplates.addAll(Arrays.asList(sFullCombinations));
Emilian Peev275da0b2018-11-15 18:20:24 +0000731
732 if (isPrivateReprocessingSupported()) {
733 availableTemplates.addAll(Arrays.asList(sFullPrivateReprocCombinations));
734 }
735
736 if (isYUVReprocessingSupported()) {
737 availableTemplates.addAll(Arrays.asList(sFullYUVReprocCombinations));
738 }
739
Emilian Peev423cbd72018-11-10 18:37:45 +0000740 }
741
742 if (isCapabilitySupported(
743 CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW)) {
744 availableTemplates.addAll(Arrays.asList(sRawCombinations));
Emilian Peev275da0b2018-11-15 18:20:24 +0000745
746 if (isPrivateReprocessingSupported()) {
747 availableTemplates.addAll(Arrays.asList(sRAWPrivateReprocCombinations));
748 }
749
750 if (isYUVReprocessingSupported()) {
751 availableTemplates.addAll(Arrays.asList(sRAWYUVReprocCombinations));
752 }
753
Emilian Peev423cbd72018-11-10 18:37:45 +0000754 }
755
756 if (isHardwareLevelAtLeastLevel3()) {
757 availableTemplates.addAll(Arrays.asList(sLevel3Combinations));
Emilian Peev275da0b2018-11-15 18:20:24 +0000758
759 if (isPrivateReprocessingSupported()) {
760 availableTemplates.addAll(Arrays.asList(sLevel3PrivateReprocCombinations));
761 }
762
763 if (isYUVReprocessingSupported()) {
764 availableTemplates.addAll(Arrays.asList(sLevel3YUVReprocCombinations));
765 }
766
Emilian Peev423cbd72018-11-10 18:37:45 +0000767 }
768
769 return generateAvailableCombinations(availableTemplates);
770 }
771
772 /**
773 * Helper method to generate the available stream combinations given the
774 * list of available combination templates.
775 *
776 * @param availableTemplates a list of templates supported by the camera device.
777 * @return a non-modifiable list of supported mandatory stream combinations or
778 * null in case of errors.
779 */
Emilian Peev0081bcf2019-02-26 14:55:58 -0800780 private @Nullable List<MandatoryStreamCombination> generateAvailableCombinations(
781 @NonNull ArrayList<StreamCombinationTemplate> availableTemplates) {
Emilian Peev423cbd72018-11-10 18:37:45 +0000782 if (availableTemplates.isEmpty()) {
783 Log.e(TAG, "No available stream templates!");
784 return null;
785 }
786
787 HashMap<Pair<SizeThreshold, Integer>, List<Size>> availableSizes =
788 enumerateAvailableSizes();
789 if (availableSizes == null) {
790 Log.e(TAG, "Available size enumeration failed!");
791 return null;
792 }
793
794 // RAW only uses MAXIMUM size threshold
795 Size[] rawSizes = mStreamConfigMap.getOutputSizes(ImageFormat.RAW_SENSOR);
796 ArrayList<Size> availableRawSizes = new ArrayList<Size>();
797 if (rawSizes != null) {
798 availableRawSizes.ensureCapacity(rawSizes.length);
799 availableRawSizes.addAll(Arrays.asList(rawSizes));
800 }
801
Emilian Peev275da0b2018-11-15 18:20:24 +0000802 Size maxPrivateInputSize = new Size(0, 0);
803 if (isPrivateReprocessingSupported()) {
804 maxPrivateInputSize = getMaxSize(mStreamConfigMap.getInputSizes(
805 ImageFormat.PRIVATE));
806 }
807
808 Size maxYUVInputSize = new Size(0, 0);
809 if (isYUVReprocessingSupported()) {
810 maxYUVInputSize = getMaxSize(mStreamConfigMap.getInputSizes(
811 ImageFormat.YUV_420_888));
812 }
813
Emilian Peev423cbd72018-11-10 18:37:45 +0000814 // Generate the available mandatory stream combinations given the supported templates
815 // and size ranges.
816 ArrayList<MandatoryStreamCombination> availableStreamCombinations =
817 new ArrayList<MandatoryStreamCombination>();
818 availableStreamCombinations.ensureCapacity(availableTemplates.size());
819 for (StreamCombinationTemplate combTemplate : availableTemplates) {
820 ArrayList<MandatoryStreamInformation> streamsInfo =
821 new ArrayList<MandatoryStreamInformation>();
822 streamsInfo.ensureCapacity(combTemplate.mStreamTemplates.length);
Emilian Peev275da0b2018-11-15 18:20:24 +0000823 boolean isReprocessable = combTemplate.mReprocessType != ReprocessType.NONE;
824 if (isReprocessable) {
825 // The first and second streams in a reprocessable combination have the
826 // same size and format. The first is the input and the second is the output
827 // used for generating the subsequent input buffers.
828 ArrayList<Size> inputSize = new ArrayList<Size>();
829 int format;
830 if (combTemplate.mReprocessType == ReprocessType.PRIVATE) {
831 inputSize.add(maxPrivateInputSize);
832 format = ImageFormat.PRIVATE;
833 } else {
834 inputSize.add(maxYUVInputSize);
835 format = ImageFormat.YUV_420_888;
836 }
837
838 streamsInfo.add(new MandatoryStreamInformation(inputSize, format,
839 /*isInput*/true));
840 streamsInfo.add(new MandatoryStreamInformation(inputSize, format));
841 }
842
Emilian Peev423cbd72018-11-10 18:37:45 +0000843 for (StreamTemplate template : combTemplate.mStreamTemplates) {
844 List<Size> sizes = null;
845 if (template.mFormat == ImageFormat.RAW_SENSOR) {
846 sizes = availableRawSizes;
847 } else {
848 Pair<SizeThreshold, Integer> pair;
849 pair = new Pair<SizeThreshold, Integer>(template.mSizeThreshold,
850 new Integer(template.mFormat));
851 sizes = availableSizes.get(pair);
852 }
853
854 MandatoryStreamInformation streamInfo;
855 try {
856 streamInfo = new MandatoryStreamInformation(sizes, template.mFormat);
857 } catch (IllegalArgumentException e) {
858 Log.e(TAG, "No available sizes found for format: " + template.mFormat +
859 " size threshold: " + template.mSizeThreshold + " combination: " +
860 combTemplate.mDescription);
861 return null;
862 }
863
864 streamsInfo.add(streamInfo);
865 }
866
867 MandatoryStreamCombination streamCombination;
868 try {
869 streamCombination = new MandatoryStreamCombination(streamsInfo,
Emilian Peev275da0b2018-11-15 18:20:24 +0000870 combTemplate.mDescription, isReprocessable);
Emilian Peev423cbd72018-11-10 18:37:45 +0000871 } catch (IllegalArgumentException e) {
872 Log.e(TAG, "No stream information for mandatory combination: "
873 + combTemplate.mDescription);
874 return null;
875 }
876
877 availableStreamCombinations.add(streamCombination);
878 }
879
880 return Collections.unmodifiableList(availableStreamCombinations);
881 }
882
883 /**
884 * Helper method to enumerate all available sizes according to size threshold and format.
885 */
Emilian Peev0081bcf2019-02-26 14:55:58 -0800886 private @Nullable HashMap<Pair<SizeThreshold, Integer>, List<Size>>
887 enumerateAvailableSizes() {
Emilian Peev423cbd72018-11-10 18:37:45 +0000888 final int[] formats = {
889 ImageFormat.PRIVATE,
890 ImageFormat.YUV_420_888,
891 ImageFormat.JPEG
892 };
893 Size recordingMaxSize = new Size(0, 0);
894 Size previewMaxSize = new Size(0, 0);
895 Size vgaSize = new Size(640, 480);
896 if (isExternalCamera()) {
897 recordingMaxSize = getMaxExternalRecordingSize();
898 } else {
899 recordingMaxSize = getMaxRecordingSize();
900 }
901 if (recordingMaxSize == null) {
902 Log.e(TAG, "Failed to find maximum recording size!");
903 return null;
904 }
905
906 HashMap<Integer, Size[]> allSizes = new HashMap<Integer, Size[]>();
907 for (int format : formats) {
908 Integer intFormat = new Integer(format);
909 allSizes.put(intFormat, mStreamConfigMap.getOutputSizes(format));
910 }
911
912 List<Size> previewSizes = getSizesWithinBound(
913 allSizes.get(new Integer(ImageFormat.PRIVATE)), kPreviewSizeBound);
914 if ((previewSizes == null) || (previewSizes.isEmpty())) {
915 Log.e(TAG, "No preview sizes within preview size bound!");
916 return null;
917 }
918 List<Size> orderedPreviewSizes = getAscendingOrderSizes(previewSizes,
919 /*ascending*/false);
920 previewMaxSize = getMaxPreviewSize(orderedPreviewSizes);
921
922 HashMap<Pair<SizeThreshold, Integer>, List<Size>> availableSizes =
923 new HashMap<Pair<SizeThreshold, Integer>, List<Size>>();
924
925 for (int format : formats) {
926 Integer intFormat = new Integer(format);
927 Size[] sizes = allSizes.get(intFormat);
928 Pair<SizeThreshold, Integer> pair = new Pair<SizeThreshold, Integer>(
929 SizeThreshold.VGA, intFormat);
930 availableSizes.put(pair, getSizesWithinBound(sizes, vgaSize));
931
932 pair = new Pair<SizeThreshold, Integer>(SizeThreshold.PREVIEW, intFormat);
933 availableSizes.put(pair, getSizesWithinBound(sizes, previewMaxSize));
934
935 pair = new Pair<SizeThreshold, Integer>(SizeThreshold.RECORD, intFormat);
936 availableSizes.put(pair, getSizesWithinBound(sizes, recordingMaxSize));
937
938 pair = new Pair<SizeThreshold, Integer>(SizeThreshold.MAXIMUM, intFormat);
939 availableSizes.put(pair, Arrays.asList(sizes));
940 }
941
942 return availableSizes;
943 }
944
945 /**
946 * Compile a list of sizes smaller than or equal to given bound.
947 * Return an empty list if there is no size smaller than or equal to the bound.
948 */
Emilian Peev0081bcf2019-02-26 14:55:58 -0800949 private static @Nullable List<Size> getSizesWithinBound(@NonNull Size[] sizes,
950 @NonNull Size bound) {
Emilian Peev423cbd72018-11-10 18:37:45 +0000951 ArrayList<Size> ret = new ArrayList<Size>();
952 for (Size size : sizes) {
953 if (size.getWidth() <= bound.getWidth() && size.getHeight() <= bound.getHeight()) {
954 ret.add(size);
955 }
956 }
957
958 return ret;
959 }
960
961 /**
Emilian Peev275da0b2018-11-15 18:20:24 +0000962 * Get the largest size by area.
963 *
964 * @param sizes an array of sizes, must have at least 1 element
965 *
966 * @return Largest Size
967 *
968 * @throws IllegalArgumentException if sizes was null or had 0 elements
969 */
Emilian Peev0081bcf2019-02-26 14:55:58 -0800970 public static @Nullable Size getMaxSize(@NonNull Size... sizes) {
Emilian Peev275da0b2018-11-15 18:20:24 +0000971 if (sizes == null || sizes.length == 0) {
972 throw new IllegalArgumentException("sizes was empty");
973 }
974
975 Size sz = sizes[0];
976 for (Size size : sizes) {
977 if (size.getWidth() * size.getHeight() > sz.getWidth() * sz.getHeight()) {
978 sz = size;
979 }
980 }
981
982 return sz;
983 }
984
985 /**
Emilian Peev423cbd72018-11-10 18:37:45 +0000986 * Whether or not the hardware level reported by android.info.supportedHardwareLevel is
987 * at least the desired one (but could be higher)
988 */
989 private boolean isHardwareLevelAtLeast(int level) {
990 final int[] sortedHwLevels = {
991 CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY,
992 CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_EXTERNAL,
993 CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
994 CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL,
995 CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_3
996 };
997 if (level == mHwLevel) {
998 return true;
999 }
1000
1001 for (int sortedlevel : sortedHwLevels) {
1002 if (sortedlevel == level) {
1003 return true;
1004 } else if (sortedlevel == mHwLevel) {
1005 return false;
1006 }
1007 }
1008
1009 return false;
1010 }
1011
1012 /**
1013 * Whether or not the camera is an external camera.
1014 *
1015 * @return {@code true} if the device is external, {@code false} otherwise.
1016 */
1017 private boolean isExternalCamera() {
1018 return mHwLevel == CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_EXTERNAL;
1019 }
1020
1021 /**
1022 * Whether or not the hardware level is at least legacy.
1023 *
1024 * @return {@code true} if the device is {@code LEGACY}, {@code false} otherwise.
1025 */
1026 private boolean isHardwareLevelAtLeastLegacy() {
1027 return isHardwareLevelAtLeast(CameraMetadata.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY);
1028 }
1029
1030 /**
1031 * Whether or not the hardware level is at least limited.
1032 *
1033 * @return {@code true} if the device is {@code LIMITED} or {@code FULL},
1034 * {@code false} otherwise (i.e. LEGACY).
1035 */
1036 private boolean isHardwareLevelAtLeastLimited() {
1037 return isHardwareLevelAtLeast(CameraMetadata.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED);
1038 }
1039
1040 /**
1041 * Whether or not the hardware level is at least full.
1042 *
1043 * @return {@code true} if the device is {@code FULL}, {@code false} otherwise.
1044 */
1045 private boolean isHardwareLevelAtLeastFull() {
1046 return isHardwareLevelAtLeast(CameraMetadata.INFO_SUPPORTED_HARDWARE_LEVEL_FULL);
1047 }
1048
1049 /**
1050 * Whether or not the hardware level is at least Level 3.
1051 *
1052 * @return {@code true} if the device is {@code LEVEL3}, {@code false} otherwise.
1053 */
1054 private boolean isHardwareLevelAtLeastLevel3() {
1055 return isHardwareLevelAtLeast(CameraMetadata.INFO_SUPPORTED_HARDWARE_LEVEL_3);
1056 }
1057
1058 /**
1059 * Determine whether the current device supports a capability or not.
1060 *
1061 * @param capability (non-negative)
1062 *
1063 * @return {@code true} if the capability is supported, {@code false} otherwise.
1064 *
1065 */
1066 private boolean isCapabilitySupported(int capability) {
1067 return mCapabilities.contains(capability);
1068 }
1069
1070 /**
1071 * Check whether the current device is backward compatible.
1072 */
1073 private boolean isColorOutputSupported() {
1074 return isCapabilitySupported(
1075 CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE);
1076 }
1077
1078 /**
Emilian Peev275da0b2018-11-15 18:20:24 +00001079 * Check whether the current device supports private reprocessing.
1080 */
1081 private boolean isPrivateReprocessingSupported() {
1082 return isCapabilitySupported(
1083 CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_PRIVATE_REPROCESSING);
1084 }
1085
1086 /**
1087 * Check whether the current device supports YUV reprocessing.
1088 */
1089 private boolean isYUVReprocessingSupported() {
1090 return isCapabilitySupported(
1091 CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_YUV_REPROCESSING);
1092 }
1093
1094 /**
Emilian Peev423cbd72018-11-10 18:37:45 +00001095 * Return the maximum supported video size using the camcorder profile information.
1096 *
1097 * @return Maximum supported video size.
1098 */
Emilian Peev0081bcf2019-02-26 14:55:58 -08001099 private @Nullable Size getMaxRecordingSize() {
Emilian Peev423cbd72018-11-10 18:37:45 +00001100 int quality =
1101 CamcorderProfile.hasProfile(mCameraId, CamcorderProfile.QUALITY_2160P) ?
1102 CamcorderProfile.QUALITY_2160P :
1103 CamcorderProfile.hasProfile(mCameraId, CamcorderProfile.QUALITY_1080P) ?
1104 CamcorderProfile.QUALITY_1080P :
1105 CamcorderProfile.hasProfile(mCameraId, CamcorderProfile.QUALITY_720P) ?
1106 CamcorderProfile.QUALITY_720P :
1107 CamcorderProfile.hasProfile(mCameraId, CamcorderProfile.QUALITY_480P) ?
1108 CamcorderProfile.QUALITY_480P :
1109 CamcorderProfile.hasProfile(mCameraId, CamcorderProfile.QUALITY_QVGA) ?
1110 CamcorderProfile.QUALITY_QVGA :
1111 CamcorderProfile.hasProfile(mCameraId, CamcorderProfile.QUALITY_CIF) ?
1112 CamcorderProfile.QUALITY_CIF :
1113 CamcorderProfile.hasProfile(mCameraId, CamcorderProfile.QUALITY_QCIF) ?
1114 CamcorderProfile.QUALITY_QCIF :
1115 -1;
1116
1117 if (quality < 0) {
1118 return null;
1119 }
1120
1121 CamcorderProfile maxProfile = CamcorderProfile.get(mCameraId, quality);
1122 return new Size(maxProfile.videoFrameWidth, maxProfile.videoFrameHeight);
1123 }
1124
1125 /**
1126 * Return the maximum supported video size for external cameras using data from
1127 * the stream configuration map.
1128 *
1129 * @return Maximum supported video size.
1130 */
Emilian Peev0081bcf2019-02-26 14:55:58 -08001131 private @NonNull Size getMaxExternalRecordingSize() {
Emilian Peev423cbd72018-11-10 18:37:45 +00001132 final Size FULLHD = new Size(1920, 1080);
1133
1134 Size[] videoSizeArr = mStreamConfigMap.getOutputSizes(
1135 android.media.MediaRecorder.class);
1136 List<Size> sizes = new ArrayList<Size>();
1137 for (Size sz: videoSizeArr) {
1138 if (sz.getWidth() <= FULLHD.getWidth() && sz.getHeight() <= FULLHD.getHeight()) {
1139 sizes.add(sz);
1140 }
1141 }
1142 List<Size> videoSizes = getAscendingOrderSizes(sizes, /*ascending*/false);
1143 for (Size sz : videoSizes) {
1144 long minFrameDuration = mStreamConfigMap.getOutputMinFrameDuration(
1145 android.media.MediaRecorder.class, sz);
1146 // Give some margin for rounding error
1147 if (minFrameDuration > (1e9 / 30.1)) {
1148 Log.i(TAG, "External camera " + mCameraId + " has max video size:" + sz);
1149 return sz;
1150 }
1151 }
1152 Log.w(TAG, "Camera " + mCameraId + " does not support any 30fps video output");
1153 return FULLHD; // doesn't matter what size is returned here
1154 }
1155
Emilian Peev0081bcf2019-02-26 14:55:58 -08001156 private @NonNull Size getMaxPreviewSize(List<Size> orderedPreviewSizes) {
Emilian Peev423cbd72018-11-10 18:37:45 +00001157 if (orderedPreviewSizes != null) {
1158 for (Size size : orderedPreviewSizes) {
1159 if ((mDisplaySize.getWidth() >= size.getWidth()) &&
1160 (mDisplaySize.getWidth() >= size.getHeight())) {
1161 return size;
1162 }
1163 }
1164 }
1165
1166 Log.w(TAG,"Camera " + mCameraId + " maximum preview size search failed with "
1167 + "display size " + mDisplaySize);
1168 return kPreviewSizeBound;
1169 }
1170
1171 /**
1172 * Size comparison method used by size comparators.
1173 */
1174 private static int compareSizes(int widthA, int heightA, int widthB, int heightB) {
1175 long left = widthA * (long) heightA;
1176 long right = widthB * (long) heightB;
1177 if (left == right) {
1178 left = widthA;
1179 right = widthB;
1180 }
1181 return (left < right) ? -1 : (left > right ? 1 : 0);
1182 }
1183
1184 /**
1185 * Size comparator that compares the number of pixels it covers.
1186 *
1187 * <p>If two the areas of two sizes are same, compare the widths.</p>
1188 */
1189 public static class SizeComparator implements Comparator<Size> {
1190 @Override
Emilian Peev0081bcf2019-02-26 14:55:58 -08001191 public int compare(@NonNull Size lhs, @NonNull Size rhs) {
Emilian Peev423cbd72018-11-10 18:37:45 +00001192 return compareSizes(lhs.getWidth(), lhs.getHeight(), rhs.getWidth(),
1193 rhs.getHeight());
1194 }
1195 }
1196
1197 /**
1198 * Get a sorted list of sizes from a given size list.
1199 *
1200 * <p>
1201 * The size is compare by area it covers, if the areas are same, then
1202 * compare the widths.
1203 * </p>
1204 *
1205 * @param sizeList The input size list to be sorted
1206 * @param ascending True if the order is ascending, otherwise descending order
1207 * @return The ordered list of sizes
1208 */
Emilian Peev0081bcf2019-02-26 14:55:58 -08001209 private static @NonNull List<Size> getAscendingOrderSizes(
1210 @NonNull final List<Size> sizeList, boolean ascending) {
Emilian Peev423cbd72018-11-10 18:37:45 +00001211 Comparator<Size> comparator = new SizeComparator();
1212 List<Size> sortedSizes = new ArrayList<Size>();
1213 sortedSizes.addAll(sizeList);
1214 Collections.sort(sortedSizes, comparator);
1215 if (!ascending) {
1216 Collections.reverse(sortedSizes);
1217 }
1218
1219 return sortedSizes;
1220 }
1221 }
1222}