1 /* 2 * Copyright 2019 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 androidx.camera.camera2.internal; 18 19 import static android.hardware.camera2.CameraCharacteristics.CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES; 20 import static android.hardware.camera2.CameraMetadata.CONTROL_VIDEO_STABILIZATION_MODE_ON; 21 import static android.hardware.camera2.CameraMetadata.CONTROL_VIDEO_STABILIZATION_MODE_PREVIEW_STABILIZATION; 22 import static android.hardware.camera2.CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_CONSTRAINED_HIGH_SPEED_VIDEO; 23 import static android.hardware.camera2.CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA; 24 import static android.hardware.camera2.CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_PRIVATE_REPROCESSING; 25 import static android.hardware.camera2.CameraMetadata.SENSOR_INFO_TIMESTAMP_SOURCE_REALTIME; 26 import static android.hardware.camera2.CameraMetadata.SENSOR_INFO_TIMESTAMP_SOURCE_UNKNOWN; 27 28 import static androidx.camera.camera2.internal.ZslUtil.isCapabilitySupported; 29 30 import android.annotation.SuppressLint; 31 import android.hardware.camera2.CameraCharacteristics; 32 import android.hardware.camera2.CameraMetadata; 33 import android.os.Build; 34 import android.util.Pair; 35 import android.util.Range; 36 import android.util.Size; 37 import android.view.Surface; 38 39 import androidx.annotation.FloatRange; 40 import androidx.annotation.GuardedBy; 41 import androidx.annotation.IntRange; 42 import androidx.annotation.OptIn; 43 import androidx.camera.camera2.internal.compat.CameraAccessExceptionCompat; 44 import androidx.camera.camera2.internal.compat.CameraCharacteristicsCompat; 45 import androidx.camera.camera2.internal.compat.CameraManagerCompat; 46 import androidx.camera.camera2.internal.compat.StreamConfigurationMapCompat; 47 import androidx.camera.camera2.internal.compat.params.DynamicRangesCompat; 48 import androidx.camera.camera2.internal.compat.quirk.CameraQuirks; 49 import androidx.camera.camera2.internal.compat.quirk.DeviceQuirks; 50 import androidx.camera.camera2.internal.compat.quirk.ZslDisablerQuirk; 51 import androidx.camera.camera2.internal.compat.workaround.FlashAvailabilityChecker; 52 import androidx.camera.camera2.interop.Camera2CameraInfo; 53 import androidx.camera.camera2.interop.ExperimentalCamera2Interop; 54 import androidx.camera.core.CameraInfo; 55 import androidx.camera.core.CameraSelector; 56 import androidx.camera.core.CameraState; 57 import androidx.camera.core.DynamicRange; 58 import androidx.camera.core.ExposureState; 59 import androidx.camera.core.FocusMeteringAction; 60 import androidx.camera.core.Logger; 61 import androidx.camera.core.ZoomState; 62 import androidx.camera.core.impl.CameraCaptureCallback; 63 import androidx.camera.core.impl.CameraInfoInternal; 64 import androidx.camera.core.impl.DynamicRanges; 65 import androidx.camera.core.impl.EncoderProfilesProvider; 66 import androidx.camera.core.impl.ImageOutputConfig.RotationValue; 67 import androidx.camera.core.impl.Quirks; 68 import androidx.camera.core.impl.Timebase; 69 import androidx.camera.core.impl.utils.CameraOrientationUtil; 70 import androidx.camera.core.impl.utils.RedirectableLiveData; 71 import androidx.core.util.Preconditions; 72 import androidx.lifecycle.LiveData; 73 74 import org.jspecify.annotations.NonNull; 75 import org.jspecify.annotations.Nullable; 76 77 import java.util.ArrayList; 78 import java.util.Arrays; 79 import java.util.Collections; 80 import java.util.HashSet; 81 import java.util.Iterator; 82 import java.util.LinkedHashMap; 83 import java.util.List; 84 import java.util.Map; 85 import java.util.Objects; 86 import java.util.Set; 87 import java.util.concurrent.Executor; 88 89 /** 90 * Implementation of the {@link CameraInfoInternal} interface that exposes parameters through 91 * camera2. 92 * 93 * <p>Construction consists of two stages. The constructor creates a implementation without a 94 * {@link Camera2CameraControlImpl} and will return default values for camera control related 95 * states like zoom/exposure/torch. After {@link #linkWithCameraControl} is called, 96 * zoom/exposure/torch API will reflect the states in the {@link Camera2CameraControlImpl}. Any 97 * CameraCaptureCallbacks added before this link will also be added 98 * to the {@link Camera2CameraControlImpl}. 99 */ 100 @OptIn(markerClass = ExperimentalCamera2Interop.class) 101 public final class Camera2CameraInfoImpl implements CameraInfoInternal { 102 103 private static final String TAG = "Camera2CameraInfo"; 104 private final String mCameraId; 105 private final CameraCharacteristicsCompat mCameraCharacteristicsCompat; 106 private final Camera2CameraInfo mCamera2CameraInfo; 107 108 private final Object mLock = new Object(); 109 @GuardedBy("mLock") 110 private @Nullable Camera2CameraControlImpl mCamera2CameraControlImpl; 111 @GuardedBy("mLock") 112 private @Nullable RedirectableLiveData<Integer> mRedirectTorchStateLiveData = null; 113 @GuardedBy("mLock") 114 private @Nullable RedirectableLiveData<Integer> mRedirectTorchStrengthLiveData = null; 115 @GuardedBy("mLock") 116 private @Nullable RedirectableLiveData<Integer> mRedirectLowLightBoostStateLiveData = null; 117 @GuardedBy("mLock") 118 private @Nullable RedirectableLiveData<ZoomState> mRedirectZoomStateLiveData = null; 119 private final @NonNull RedirectableLiveData<CameraState> mCameraStateLiveData; 120 @GuardedBy("mLock") 121 private @Nullable List<Pair<CameraCaptureCallback, Executor>> mCameraCaptureCallbacks = null; 122 123 private final @NonNull Quirks mCameraQuirks; 124 private final @NonNull EncoderProfilesProvider mCamera2EncoderProfilesProvider; 125 private final @NonNull CameraManagerCompat mCameraManager; 126 127 private @Nullable Set<CameraInfo> mPhysicalCameraInfos; 128 129 /** 130 * Constructs an instance. Before {@link #linkWithCameraControl(Camera2CameraControlImpl)} is 131 * called, camera control related API (torch/exposure/zoom) will return default values. 132 */ Camera2CameraInfoImpl(@onNull String cameraId, @NonNull CameraManagerCompat cameraManager)133 public Camera2CameraInfoImpl(@NonNull String cameraId, 134 @NonNull CameraManagerCompat cameraManager) throws CameraAccessExceptionCompat { 135 mCameraId = Preconditions.checkNotNull(cameraId); 136 mCameraManager = cameraManager; 137 138 mCameraCharacteristicsCompat = cameraManager.getCameraCharacteristicsCompat(mCameraId); 139 mCamera2CameraInfo = new Camera2CameraInfo(this); 140 mCameraQuirks = CameraQuirks.get(cameraId, mCameraCharacteristicsCompat); 141 mCamera2EncoderProfilesProvider = new Camera2EncoderProfilesProvider(cameraId, 142 mCameraQuirks); 143 mCameraStateLiveData = new RedirectableLiveData<>( 144 CameraState.create(CameraState.Type.CLOSED)); 145 } 146 147 /** 148 * Links with a {@link Camera2CameraControlImpl}. After the link, zoom/torch/exposure 149 * operations of CameraControl will modify the states in this Camera2CameraInfoImpl. 150 * Also, any CameraCaptureCallbacks added before this link will be added to the 151 * {@link Camera2CameraControlImpl}. 152 */ linkWithCameraControl(@onNull Camera2CameraControlImpl camera2CameraControlImpl)153 void linkWithCameraControl(@NonNull Camera2CameraControlImpl camera2CameraControlImpl) { 154 synchronized (mLock) { 155 mCamera2CameraControlImpl = camera2CameraControlImpl; 156 157 if (mRedirectZoomStateLiveData != null) { 158 mRedirectZoomStateLiveData.redirectTo( 159 mCamera2CameraControlImpl.getZoomControl().getZoomState()); 160 } 161 162 if (mRedirectTorchStateLiveData != null) { 163 mRedirectTorchStateLiveData.redirectTo( 164 mCamera2CameraControlImpl.getTorchControl().getTorchState()); 165 } 166 167 if (mRedirectTorchStrengthLiveData != null) { 168 mRedirectTorchStrengthLiveData.redirectTo( 169 mCamera2CameraControlImpl.getTorchControl().getTorchStrengthLevel()); 170 } 171 172 if (mRedirectLowLightBoostStateLiveData != null) { 173 mRedirectLowLightBoostStateLiveData.redirectTo(mCamera2CameraControlImpl 174 .getLowLightBoostControl().getLowLightBoostState()); 175 } 176 177 if (mCameraCaptureCallbacks != null) { 178 for (Pair<CameraCaptureCallback, Executor> pair : 179 mCameraCaptureCallbacks) { 180 mCamera2CameraControlImpl.addSessionCameraCaptureCallback(pair.second, 181 pair.first); 182 } 183 mCameraCaptureCallbacks = null; 184 } 185 } 186 logDeviceInfo(); 187 } 188 189 /** 190 * Sets the source of the {@linkplain CameraState camera states} that will be exposed. When 191 * called more than once, the previous camera state source is overridden. 192 */ setCameraStateSource(@onNull LiveData<CameraState> cameraStateSource)193 void setCameraStateSource(@NonNull LiveData<CameraState> cameraStateSource) { 194 mCameraStateLiveData.redirectTo(cameraStateSource); 195 } 196 197 @Override getCameraId()198 public @NonNull String getCameraId() { 199 return mCameraId; 200 } 201 getCameraCharacteristicsCompat()202 public @NonNull CameraCharacteristicsCompat getCameraCharacteristicsCompat() { 203 return mCameraCharacteristicsCompat; 204 } 205 206 @CameraSelector.LensFacing 207 @Override getLensFacing()208 public int getLensFacing() { 209 Integer lensFacing = mCameraCharacteristicsCompat.get(CameraCharacteristics.LENS_FACING); 210 Preconditions.checkArgument(lensFacing != null, "Unable to get the lens facing of the " 211 + "camera."); 212 return LensFacingUtil.getCameraSelectorLensFacing(lensFacing); 213 } 214 215 @Override getSensorRotationDegrees(@otationValue int relativeRotation)216 public int getSensorRotationDegrees(@RotationValue int relativeRotation) { 217 int sensorOrientation = getSensorOrientation(); 218 int relativeRotationDegrees = 219 CameraOrientationUtil.surfaceRotationToDegrees(relativeRotation); 220 // Currently this assumes that a back-facing camera is always opposite to the screen. 221 // This may not be the case for all devices, so in the future we may need to handle that 222 // scenario. 223 final int lensFacing = getLensFacing(); 224 boolean isOppositeFacingScreen = CameraSelector.LENS_FACING_BACK == lensFacing; 225 return CameraOrientationUtil.getRelativeImageRotation( 226 relativeRotationDegrees, 227 sensorOrientation, 228 isOppositeFacingScreen); 229 } 230 getSensorOrientation()231 int getSensorOrientation() { 232 Integer sensorOrientation = 233 mCameraCharacteristicsCompat.get(CameraCharacteristics.SENSOR_ORIENTATION); 234 Preconditions.checkNotNull(sensorOrientation); 235 return sensorOrientation; 236 } 237 getSupportedHardwareLevel()238 int getSupportedHardwareLevel() { 239 Integer deviceLevel = 240 mCameraCharacteristicsCompat.get( 241 CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL); 242 Preconditions.checkNotNull(deviceLevel); 243 return deviceLevel; 244 } 245 246 @Override getSensorRotationDegrees()247 public int getSensorRotationDegrees() { 248 return getSensorRotationDegrees(Surface.ROTATION_0); 249 } 250 logDeviceInfo()251 private void logDeviceInfo() { 252 // Extend by adding logging here as needed. 253 logDeviceLevel(); 254 } 255 logDeviceLevel()256 private void logDeviceLevel() { 257 String levelString; 258 259 int deviceLevel = getSupportedHardwareLevel(); 260 switch (deviceLevel) { 261 case CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY: 262 levelString = "INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY"; 263 break; 264 case CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_EXTERNAL: 265 levelString = "INFO_SUPPORTED_HARDWARE_LEVEL_EXTERNAL"; 266 break; 267 case CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED: 268 levelString = "INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED"; 269 break; 270 case CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL: 271 levelString = "INFO_SUPPORTED_HARDWARE_LEVEL_FULL"; 272 break; 273 case CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_3: 274 levelString = "INFO_SUPPORTED_HARDWARE_LEVEL_3"; 275 break; 276 default: 277 levelString = "Unknown value: " + deviceLevel; 278 break; 279 } 280 Logger.i(TAG, "Device Level: " + levelString); 281 } 282 283 @Override hasFlashUnit()284 public boolean hasFlashUnit() { 285 return FlashAvailabilityChecker.isFlashAvailable(mCameraCharacteristicsCompat::get); 286 } 287 288 @Override getTorchState()289 public @NonNull LiveData<Integer> getTorchState() { 290 synchronized (mLock) { 291 if (mCamera2CameraControlImpl == null) { 292 if (mRedirectTorchStateLiveData == null) { 293 mRedirectTorchStateLiveData = 294 new RedirectableLiveData<>(TorchControl.DEFAULT_TORCH_STATE); 295 } 296 return mRedirectTorchStateLiveData; 297 } 298 299 // if RedirectableLiveData exists, use it directly. 300 if (mRedirectTorchStateLiveData != null) { 301 return mRedirectTorchStateLiveData; 302 } 303 304 return mCamera2CameraControlImpl.getTorchControl().getTorchState(); 305 } 306 } 307 308 @Override isLowLightBoostSupported()309 public boolean isLowLightBoostSupported() { 310 return LowLightBoostControl.checkLowLightBoostAvailability(mCameraCharacteristicsCompat); 311 } 312 313 @Override getLowLightBoostState()314 public @NonNull LiveData<Integer> getLowLightBoostState() { 315 synchronized (mLock) { 316 if (mCamera2CameraControlImpl == null) { 317 if (mRedirectLowLightBoostStateLiveData == null) { 318 mRedirectLowLightBoostStateLiveData = 319 new RedirectableLiveData<>(LowLightBoostControl.DEFAULT_LLB_STATE); 320 } 321 return mRedirectLowLightBoostStateLiveData; 322 } 323 324 // if RedirectableLiveData exists, use it directly. 325 if (mRedirectLowLightBoostStateLiveData != null) { 326 return mRedirectLowLightBoostStateLiveData; 327 } 328 329 return mCamera2CameraControlImpl.getLowLightBoostControl().getLowLightBoostState(); 330 } 331 } 332 333 @Override getZoomState()334 public @NonNull LiveData<ZoomState> getZoomState() { 335 synchronized (mLock) { 336 if (mCamera2CameraControlImpl == null) { 337 if (mRedirectZoomStateLiveData == null) { 338 mRedirectZoomStateLiveData = new RedirectableLiveData<>( 339 ZoomControl.getDefaultZoomState(mCameraCharacteristicsCompat)); 340 } 341 return mRedirectZoomStateLiveData; 342 } 343 344 // if RedirectableLiveData exists, use it directly. 345 if (mRedirectZoomStateLiveData != null) { 346 return mRedirectZoomStateLiveData; 347 } 348 349 return mCamera2CameraControlImpl.getZoomControl().getZoomState(); 350 } 351 } 352 353 @Override getExposureState()354 public @NonNull ExposureState getExposureState() { 355 synchronized (mLock) { 356 if (mCamera2CameraControlImpl == null) { 357 return ExposureControl.getDefaultExposureState(mCameraCharacteristicsCompat); 358 } 359 return mCamera2CameraControlImpl.getExposureControl().getExposureState(); 360 } 361 } 362 363 @Override getCameraState()364 public @NonNull LiveData<CameraState> getCameraState() { 365 return mCameraStateLiveData; 366 } 367 368 /** 369 * {@inheritDoc} 370 * 371 * <p>When the CameraX configuration is {@link androidx.camera.camera2.Camera2Config}, the 372 * return value depends on whether the device is legacy 373 * ({@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL} {@code == 374 * }{@link CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY}). 375 * 376 * @return {@link #IMPLEMENTATION_TYPE_CAMERA2_LEGACY} if the device is legacy, otherwise 377 * {@link #IMPLEMENTATION_TYPE_CAMERA2}. 378 */ 379 @Override getImplementationType()380 public @NonNull String getImplementationType() { 381 final int hardwareLevel = getSupportedHardwareLevel(); 382 return hardwareLevel == CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY 383 ? IMPLEMENTATION_TYPE_CAMERA2_LEGACY : IMPLEMENTATION_TYPE_CAMERA2; 384 } 385 386 @FloatRange(from = 0, fromInclusive = false) 387 @Override getIntrinsicZoomRatio()388 public float getIntrinsicZoomRatio() { 389 final Integer lensFacing = 390 mCameraCharacteristicsCompat.get(CameraCharacteristics.LENS_FACING); 391 if (lensFacing == null) { 392 return INTRINSIC_ZOOM_RATIO_UNKNOWN; 393 } 394 395 int fovDegrees; 396 int defaultFovDegrees; 397 try { 398 fovDegrees = 399 FovUtil.focalLengthToViewAngleDegrees( 400 FovUtil.getDefaultFocalLength(mCameraCharacteristicsCompat), 401 FovUtil.getSensorHorizontalLength(mCameraCharacteristicsCompat)); 402 defaultFovDegrees = FovUtil.getDeviceDefaultViewAngleDegrees(mCameraManager, 403 lensFacing); 404 } catch (Exception e) { 405 Logger.e(TAG, "The camera is unable to provide necessary information to resolve its " 406 + "intrinsic zoom ratio with error: " + e); 407 return INTRINSIC_ZOOM_RATIO_UNKNOWN; 408 } 409 410 return ((float) defaultFovDegrees) / fovDegrees; 411 } 412 413 @Override isFocusMeteringSupported(@onNull FocusMeteringAction action)414 public boolean isFocusMeteringSupported(@NonNull FocusMeteringAction action) { 415 synchronized (mLock) { 416 if (mCamera2CameraControlImpl == null) { 417 return false; 418 } 419 return mCamera2CameraControlImpl.getFocusMeteringControl().isFocusMeteringSupported( 420 action); 421 } 422 } 423 424 @SuppressLint("NullAnnotationGroup") 425 @OptIn(markerClass = androidx.camera.core.ExperimentalZeroShutterLag.class) 426 @Override isZslSupported()427 public boolean isZslSupported() { 428 return Build.VERSION.SDK_INT >= 23 && isPrivateReprocessingSupported() 429 && (DeviceQuirks.get(ZslDisablerQuirk.class) == null); 430 } 431 432 @Override isPrivateReprocessingSupported()433 public boolean isPrivateReprocessingSupported() { 434 return isCapabilitySupported(mCameraCharacteristicsCompat, 435 REQUEST_AVAILABLE_CAPABILITIES_PRIVATE_REPROCESSING); 436 } 437 438 @Override isLogicalMultiCameraSupported()439 public boolean isLogicalMultiCameraSupported() { 440 return isCapabilitySupported(mCameraCharacteristicsCompat, 441 REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA); 442 } 443 444 /** {@inheritDoc} */ 445 @Override getEncoderProfilesProvider()446 public @NonNull EncoderProfilesProvider getEncoderProfilesProvider() { 447 return mCamera2EncoderProfilesProvider; 448 } 449 450 @Override getTimebase()451 public @NonNull Timebase getTimebase() { 452 Integer timeSource = mCameraCharacteristicsCompat.get( 453 CameraCharacteristics.SENSOR_INFO_TIMESTAMP_SOURCE); 454 Preconditions.checkNotNull(timeSource); 455 switch (timeSource) { 456 case SENSOR_INFO_TIMESTAMP_SOURCE_REALTIME: 457 return Timebase.REALTIME; 458 case SENSOR_INFO_TIMESTAMP_SOURCE_UNKNOWN: 459 default: 460 return Timebase.UPTIME; 461 } 462 } 463 464 @Override getSupportedOutputFormats()465 public @NonNull Set<Integer> getSupportedOutputFormats() { 466 StreamConfigurationMapCompat mapCompat = 467 mCameraCharacteristicsCompat.getStreamConfigurationMapCompat(); 468 int[] formats = mapCompat.getOutputFormats(); 469 if (formats == null) { 470 return new HashSet<>(); 471 } 472 473 Set<Integer> result = new HashSet<>(); 474 for (int format : formats) { 475 result.add(format); 476 } 477 return result; 478 } 479 480 @Override getSupportedResolutions(int format)481 public @NonNull List<Size> getSupportedResolutions(int format) { 482 StreamConfigurationMapCompat mapCompat = 483 mCameraCharacteristicsCompat.getStreamConfigurationMapCompat(); 484 Size[] size = mapCompat.getOutputSizes(format); 485 return size != null ? Arrays.asList(size) : Collections.emptyList(); 486 } 487 488 @Override getSupportedHighResolutions(int format)489 public @NonNull List<Size> getSupportedHighResolutions(int format) { 490 StreamConfigurationMapCompat mapCompat = 491 mCameraCharacteristicsCompat.getStreamConfigurationMapCompat(); 492 Size[] size = mapCompat.getHighResolutionOutputSizes(format); 493 return size != null ? Arrays.asList(size) : Collections.emptyList(); 494 } 495 496 @Override getSupportedDynamicRanges()497 public @NonNull Set<DynamicRange> getSupportedDynamicRanges() { 498 DynamicRangesCompat dynamicRangesCompat = DynamicRangesCompat.fromCameraCharacteristics( 499 mCameraCharacteristicsCompat); 500 501 return dynamicRangesCompat.getSupportedDynamicRanges(); 502 } 503 504 @Override isHighSpeedSupported()505 public boolean isHighSpeedSupported() { 506 return Build.VERSION.SDK_INT >= 23 && isCapabilitySupported(mCameraCharacteristicsCompat, 507 REQUEST_AVAILABLE_CAPABILITIES_CONSTRAINED_HIGH_SPEED_VIDEO); 508 } 509 510 @Override getSupportedHighSpeedFrameRateRanges()511 public @NonNull Set<Range<Integer>> getSupportedHighSpeedFrameRateRanges() { 512 Range<Integer>[] ranges = mCameraCharacteristicsCompat.getStreamConfigurationMapCompat() 513 .getHighSpeedVideoFpsRanges(); 514 return ranges != null ? new HashSet<>(Arrays.asList(ranges)) : Collections.emptySet(); 515 } 516 517 @Override getSupportedHighSpeedFrameRateRangesFor( @onNull Size size)518 public @NonNull Set<Range<Integer>> getSupportedHighSpeedFrameRateRangesFor( 519 @NonNull Size size) { 520 Range<Integer>[] ranges = null; 521 try { 522 ranges = mCameraCharacteristicsCompat.getStreamConfigurationMapCompat() 523 .getHighSpeedVideoFpsRangesFor(size); 524 } catch (IllegalArgumentException e) { 525 Logger.w(TAG, "Can't get high speed frame rate ranges for " + size, e); 526 } 527 return ranges != null ? new HashSet<>(Arrays.asList(ranges)) : Collections.emptySet(); 528 } 529 530 @Override getSupportedHighSpeedResolutions()531 public @NonNull List<Size> getSupportedHighSpeedResolutions() { 532 Size[] sizes = mCameraCharacteristicsCompat.getStreamConfigurationMapCompat() 533 .getHighSpeedVideoSizes(); 534 return sizes != null ? Arrays.asList(sizes) : Collections.emptyList(); 535 } 536 537 @Override getSupportedHighSpeedResolutionsFor( @onNull Range<Integer> fpsRange)538 public @NonNull List<Size> getSupportedHighSpeedResolutionsFor( 539 @NonNull Range<Integer> fpsRange) { 540 Size[] sizes = null; 541 try { 542 sizes = mCameraCharacteristicsCompat.getStreamConfigurationMapCompat() 543 .getHighSpeedVideoSizesFor(fpsRange); 544 } catch (IllegalArgumentException e) { 545 Logger.w(TAG, "Can't get high speed resolutions for " + fpsRange, e); 546 } 547 return sizes != null ? Arrays.asList(sizes) : Collections.emptyList(); 548 } 549 550 @Override querySupportedDynamicRanges( @onNull Set<DynamicRange> candidateDynamicRanges)551 public @NonNull Set<DynamicRange> querySupportedDynamicRanges( 552 @NonNull Set<DynamicRange> candidateDynamicRanges) { 553 return DynamicRanges.findAllPossibleMatches(candidateDynamicRanges, 554 getSupportedDynamicRanges()); 555 } 556 557 @Override addSessionCaptureCallback(@onNull Executor executor, @NonNull CameraCaptureCallback callback)558 public void addSessionCaptureCallback(@NonNull Executor executor, 559 @NonNull CameraCaptureCallback callback) { 560 synchronized (mLock) { 561 if (mCamera2CameraControlImpl == null) { 562 if (mCameraCaptureCallbacks == null) { 563 mCameraCaptureCallbacks = new ArrayList<>(); 564 } 565 mCameraCaptureCallbacks.add(new Pair<>(callback, executor)); 566 return; 567 } 568 569 mCamera2CameraControlImpl.addSessionCameraCaptureCallback(executor, callback); 570 } 571 } 572 573 @Override removeSessionCaptureCallback(@onNull CameraCaptureCallback callback)574 public void removeSessionCaptureCallback(@NonNull CameraCaptureCallback callback) { 575 synchronized (mLock) { 576 if (mCamera2CameraControlImpl == null) { 577 if (mCameraCaptureCallbacks == null) { 578 return; 579 } 580 Iterator<Pair<CameraCaptureCallback, Executor>> it = 581 mCameraCaptureCallbacks.iterator(); 582 while (it.hasNext()) { 583 Pair<CameraCaptureCallback, Executor> pair = it.next(); 584 if (pair.first == callback) { 585 it.remove(); 586 } 587 } 588 return; 589 } 590 mCamera2CameraControlImpl.removeSessionCameraCaptureCallback(callback); 591 } 592 } 593 594 /** {@inheritDoc} */ 595 @Override getCameraQuirks()596 public @NonNull Quirks getCameraQuirks() { 597 return mCameraQuirks; 598 } 599 600 @Override getSupportedFrameRateRanges()601 public @NonNull Set<Range<Integer>> getSupportedFrameRateRanges() { 602 Range<Integer>[] availableTargetFpsRanges = 603 mCameraCharacteristicsCompat.get( 604 CameraCharacteristics.CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES); 605 if (availableTargetFpsRanges != null) { 606 return new HashSet<>(Arrays.asList(availableTargetFpsRanges)); 607 } else { 608 return Collections.emptySet(); 609 } 610 } 611 612 @Override isVideoStabilizationSupported()613 public boolean isVideoStabilizationSupported() { 614 int[] availableVideoStabilizationModes = 615 mCameraCharacteristicsCompat.get( 616 CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES); 617 if (availableVideoStabilizationModes != null) { 618 for (int mode : availableVideoStabilizationModes) { 619 if (mode == CONTROL_VIDEO_STABILIZATION_MODE_ON) { 620 return true; 621 } 622 } 623 } 624 return false; 625 } 626 627 @Override isPreviewStabilizationSupported()628 public boolean isPreviewStabilizationSupported() { 629 int[] availableVideoStabilizationModes = 630 mCameraCharacteristicsCompat.get( 631 CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES); 632 if (availableVideoStabilizationModes != null) { 633 for (int mode : availableVideoStabilizationModes) { 634 if (mode == CONTROL_VIDEO_STABILIZATION_MODE_PREVIEW_STABILIZATION) { 635 return true; 636 } 637 } 638 } 639 return false; 640 } 641 642 /** 643 * Gets the implementation of {@link Camera2CameraInfo}. 644 */ getCamera2CameraInfo()645 public @NonNull Camera2CameraInfo getCamera2CameraInfo() { 646 return mCamera2CameraInfo; 647 } 648 649 @Override getCameraCharacteristics()650 public @NonNull Object getCameraCharacteristics() { 651 return mCameraCharacteristicsCompat.toCameraCharacteristics(); 652 } 653 654 @Override getPhysicalCameraCharacteristics(@onNull String physicalCameraId)655 public @Nullable Object getPhysicalCameraCharacteristics(@NonNull String physicalCameraId) { 656 try { 657 if (!mCameraCharacteristicsCompat.getPhysicalCameraIds().contains(physicalCameraId)) { 658 return null; 659 } 660 return mCameraManager.getCameraCharacteristicsCompat(physicalCameraId) 661 .toCameraCharacteristics(); 662 } catch (CameraAccessExceptionCompat e) { 663 Logger.e(TAG, 664 "Failed to get CameraCharacteristics for cameraId " + physicalCameraId, 665 e); 666 } 667 return null; 668 } 669 670 /** 671 * Returns a map consisting of the camera ids and the {@link CameraCharacteristics}s. 672 * 673 * <p>For every camera, the map contains at least the CameraCharacteristics for the camera id. 674 * If the camera is logical camera, it will also contain associated physical camera ids and 675 * their CameraCharacteristics. 676 * 677 */ getCameraCharacteristicsMap()678 public @NonNull Map<String, CameraCharacteristics> getCameraCharacteristicsMap() { 679 LinkedHashMap<String, CameraCharacteristics> map = new LinkedHashMap<>(); 680 681 map.put(mCameraId, mCameraCharacteristicsCompat.toCameraCharacteristics()); 682 683 for (String physicalCameraId : mCameraCharacteristicsCompat.getPhysicalCameraIds()) { 684 if (Objects.equals(physicalCameraId, mCameraId)) { 685 continue; 686 } 687 try { 688 map.put(physicalCameraId, 689 mCameraManager.getCameraCharacteristicsCompat(physicalCameraId) 690 .toCameraCharacteristics()); 691 } catch (CameraAccessExceptionCompat e) { 692 Logger.e(TAG, 693 "Failed to get CameraCharacteristics for cameraId " + physicalCameraId, e); 694 } 695 } 696 return map; 697 } 698 699 @Override getPhysicalCameraInfos()700 public @NonNull Set<CameraInfo> getPhysicalCameraInfos() { 701 if (mPhysicalCameraInfos == null) { 702 mPhysicalCameraInfos = new HashSet<>(); 703 for (String physicalCameraId : mCameraCharacteristicsCompat.getPhysicalCameraIds()) { 704 try { 705 CameraInfo physicalCameraInfo = new Camera2PhysicalCameraInfoImpl( 706 physicalCameraId, 707 mCameraManager); 708 mPhysicalCameraInfos.add(physicalCameraInfo); 709 } catch (CameraAccessExceptionCompat e) { 710 Logger.e(TAG, 711 "Failed to get CameraCharacteristics for cameraId " + physicalCameraId, 712 e); 713 return Collections.emptySet(); 714 } 715 } 716 } 717 718 return mPhysicalCameraInfos; 719 } 720 721 @Override 722 @IntRange(from = 0) getMaxTorchStrengthLevel()723 public int getMaxTorchStrengthLevel() { 724 return isTorchStrengthSupported() ? mCameraCharacteristicsCompat.getMaxTorchStrengthLevel() 725 : TORCH_STRENGTH_LEVEL_UNSUPPORTED; 726 } 727 728 @Override getTorchStrengthLevel()729 public @NonNull LiveData<Integer> getTorchStrengthLevel() { 730 synchronized (mLock) { 731 if (mCamera2CameraControlImpl == null) { 732 if (mRedirectTorchStrengthLiveData == null) { 733 mRedirectTorchStrengthLiveData = new RedirectableLiveData<>( 734 isTorchStrengthSupported() 735 ? mCameraCharacteristicsCompat.getDefaultTorchStrengthLevel() 736 : TORCH_STRENGTH_LEVEL_UNSUPPORTED); 737 } 738 return mRedirectTorchStrengthLiveData; 739 } 740 741 if (mRedirectTorchStrengthLiveData != null) { 742 return mRedirectTorchStrengthLiveData; 743 } 744 745 return mCamera2CameraControlImpl.getTorchControl().getTorchStrengthLevel(); 746 } 747 } 748 749 @Override isTorchStrengthSupported()750 public boolean isTorchStrengthSupported() { 751 return mCameraCharacteristicsCompat.isTorchStrengthLevelSupported(); 752 } 753 } 754