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