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