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