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