1 /* 2 * Copyright 2023 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.core.impl; 18 19 import static android.hardware.camera2.CameraMetadata.CONTROL_VIDEO_STABILIZATION_MODE_ON; 20 import static android.hardware.camera2.CameraMetadata.CONTROL_VIDEO_STABILIZATION_MODE_PREVIEW_STABILIZATION; 21 22 import android.util.Range; 23 import android.util.Rational; 24 25 import androidx.annotation.IntDef; 26 import androidx.camera.core.ExposureState; 27 import androidx.camera.core.FocusMeteringAction; 28 import androidx.camera.core.TorchState; 29 import androidx.camera.core.ZoomState; 30 import androidx.camera.core.impl.utils.LiveDataUtil; 31 import androidx.camera.core.impl.utils.SessionProcessorUtil; 32 import androidx.camera.core.internal.ImmutableZoomState; 33 import androidx.core.math.MathUtils; 34 import androidx.lifecycle.LiveData; 35 import androidx.lifecycle.MutableLiveData; 36 37 import org.jspecify.annotations.NonNull; 38 import org.jspecify.annotations.Nullable; 39 40 import java.lang.annotation.Retention; 41 import java.lang.annotation.RetentionPolicy; 42 43 /** 44 * A {@link CameraInfoInternal} that returns disabled state if the corresponding operation in the 45 * given {@link AdapterCameraControl} is disabled. 46 */ 47 public class AdapterCameraInfo extends ForwardingCameraInfo { 48 /** 49 * Defines the list of supported camera operations. 50 */ 51 public static final int CAMERA_OPERATION_ZOOM = 0; 52 public static final int CAMERA_OPERATION_AUTO_FOCUS = 1; 53 public static final int CAMERA_OPERATION_AF_REGION = 2; 54 public static final int CAMERA_OPERATION_AE_REGION = 3; 55 public static final int CAMERA_OPERATION_AWB_REGION = 4; 56 public static final int CAMERA_OPERATION_FLASH = 5; 57 public static final int CAMERA_OPERATION_TORCH = 6; 58 public static final int CAMERA_OPERATION_EXPOSURE_COMPENSATION = 7; 59 public static final int CAMERA_OPERATION_EXTENSION_STRENGTH = 8; 60 61 @IntDef({CAMERA_OPERATION_ZOOM, CAMERA_OPERATION_AUTO_FOCUS, CAMERA_OPERATION_AF_REGION, 62 CAMERA_OPERATION_AE_REGION, CAMERA_OPERATION_AWB_REGION, CAMERA_OPERATION_FLASH, 63 CAMERA_OPERATION_TORCH, CAMERA_OPERATION_EXPOSURE_COMPENSATION, 64 CAMERA_OPERATION_EXTENSION_STRENGTH}) 65 @Retention(RetentionPolicy.SOURCE) 66 public @interface CameraOperation { 67 } 68 69 private final CameraInfoInternal mCameraInfo; 70 private final @Nullable SessionProcessor mSessionProcessor; 71 private boolean mIsPostviewSupported = false; 72 private boolean mIsCaptureProcessProgressSupported = false; 73 private final @NonNull CameraConfig mCameraConfig; 74 private @Nullable LiveData<ZoomState> mExtensionZoomStateLiveData = null; 75 AdapterCameraInfo(@onNull CameraInfoInternal cameraInfo, @NonNull CameraConfig cameraConfig)76 public AdapterCameraInfo(@NonNull CameraInfoInternal cameraInfo, 77 @NonNull CameraConfig cameraConfig) { 78 super(cameraInfo); 79 mCameraInfo = cameraInfo; 80 mCameraConfig = cameraConfig; 81 mSessionProcessor = cameraConfig.getSessionProcessor(null); 82 83 setPostviewSupported(cameraConfig.isPostviewSupported()); 84 setCaptureProcessProgressSupported(cameraConfig.isCaptureProcessProgressSupported()); 85 } 86 getCameraConfig()87 public @NonNull CameraConfig getCameraConfig() { 88 return mCameraConfig; 89 } 90 91 @Override getImplementation()92 public @NonNull CameraInfoInternal getImplementation() { 93 return mCameraInfo; 94 } 95 96 /** 97 * Returns the session processor associated with the AdapterCameraInfo. 98 */ getSessionProcessor()99 public @Nullable SessionProcessor getSessionProcessor() { 100 return mSessionProcessor; 101 } 102 103 @Override hasFlashUnit()104 public boolean hasFlashUnit() { 105 if (!SessionProcessorUtil.isOperationSupported(mSessionProcessor, CAMERA_OPERATION_FLASH)) { 106 return false; 107 } 108 109 return mCameraInfo.hasFlashUnit(); 110 } 111 112 @Override getTorchState()113 public @NonNull LiveData<Integer> getTorchState() { 114 if (!SessionProcessorUtil.isOperationSupported(mSessionProcessor, CAMERA_OPERATION_TORCH)) { 115 return new MutableLiveData<>(TorchState.OFF); 116 } 117 118 return mCameraInfo.getTorchState(); 119 } 120 121 /** 122 * Return the zoom ratio calculated by the linear zoom (percentage) 123 */ getZoomRatioByPercentage(float percentage, float minZoomRatio, float maxZoomRatio)124 public static float getZoomRatioByPercentage(float percentage, 125 float minZoomRatio, float maxZoomRatio) { 126 // Make sure 1.0f and 0.0 return exactly the same max/min ratio. 127 if (percentage == 1.0f) { 128 return maxZoomRatio; 129 } else if (percentage == 0f) { 130 return minZoomRatio; 131 } 132 // This crop width is proportional to the real crop width. 133 // The real crop with = sensorWidth/ zoomRatio, but we need the ratio only so we can 134 // assume sensorWidth as 1.0f. 135 double cropWidthInMaxZoom = 1.0f / maxZoomRatio; 136 double cropWidthInMinZoom = 1.0f / minZoomRatio; 137 138 double cropWidth = cropWidthInMinZoom + (cropWidthInMaxZoom - cropWidthInMinZoom) 139 * percentage; 140 141 double ratio = 1.0 / cropWidth; 142 143 return (float) MathUtils.clamp(ratio, minZoomRatio, maxZoomRatio); 144 } 145 146 /** 147 * Return the linear zoom (percentage) calculated by the zoom ratio. 148 */ getPercentageByRatio(float ratio, float minZoomRatio, float maxZoomRatio)149 public static float getPercentageByRatio(float ratio, float minZoomRatio, float maxZoomRatio) { 150 // if zoom is not supported, return 0 151 if (maxZoomRatio == minZoomRatio) { 152 return 0f; 153 } 154 155 // To make the min/max same value when doing conversion between ratio / percentage. 156 // We return the max/min value directly. 157 if (ratio == maxZoomRatio) { 158 return 1f; 159 } else if (ratio == minZoomRatio) { 160 return 0f; 161 } 162 163 float cropWidth = 1.0f / ratio; 164 float cropWidthInMaxZoom = 1.0f / maxZoomRatio; 165 float cropWidthInMinZoom = 1.0f / minZoomRatio; 166 167 return (cropWidth - cropWidthInMinZoom) / (cropWidthInMaxZoom - cropWidthInMinZoom); 168 } 169 170 @Override getZoomState()171 public @NonNull LiveData<ZoomState> getZoomState() { 172 if (!SessionProcessorUtil.isOperationSupported(mSessionProcessor, CAMERA_OPERATION_ZOOM)) { 173 return new MutableLiveData<>(ImmutableZoomState.create( 174 /* zoomRatio */1f, /* maxZoomRatio */ 1f, 175 /* minZoomRatio */ 1f, /* linearZoom*/ 0f)); 176 } 177 178 if (mSessionProcessor != null) { 179 ZoomState zoomState = mCameraInfo.getZoomState().getValue(); 180 Range<Float> extensionsZoomRange = mSessionProcessor.getExtensionZoomRange(); 181 if (extensionsZoomRange != null 182 && (extensionsZoomRange.getLower() != zoomState.getMinZoomRatio() 183 || extensionsZoomRange.getUpper() != zoomState.getMaxZoomRatio())) { 184 if (mExtensionZoomStateLiveData == null) { 185 // Transform the zoomState to have adjusted maxzoom, minzoom and linear zoom 186 mExtensionZoomStateLiveData = LiveDataUtil.map(mCameraInfo.getZoomState(), 187 (state) -> { 188 return ImmutableZoomState.create( 189 state.getZoomRatio(), 190 extensionsZoomRange.getUpper(), 191 extensionsZoomRange.getLower(), 192 getPercentageByRatio(state.getZoomRatio(), 193 extensionsZoomRange.getLower(), 194 extensionsZoomRange.getUpper()) 195 ); 196 }); 197 } 198 return mExtensionZoomStateLiveData; 199 } 200 } 201 return mCameraInfo.getZoomState(); 202 } 203 204 @Override getExposureState()205 public @NonNull ExposureState getExposureState() { 206 if (!SessionProcessorUtil.isOperationSupported(mSessionProcessor, 207 CAMERA_OPERATION_EXPOSURE_COMPENSATION)) { 208 return new ExposureState() { 209 @Override 210 public int getExposureCompensationIndex() { 211 return 0; 212 } 213 214 @Override 215 public @NonNull Range<Integer> getExposureCompensationRange() { 216 return new Range<>(0, 0); 217 } 218 219 @Override 220 public @NonNull Rational getExposureCompensationStep() { 221 return Rational.ZERO; 222 } 223 224 @Override 225 public boolean isExposureCompensationSupported() { 226 return false; 227 } 228 }; 229 } 230 return mCameraInfo.getExposureState(); 231 } 232 233 @Override 234 public boolean isFocusMeteringSupported(@NonNull FocusMeteringAction action) { 235 FocusMeteringAction modifiedAction = 236 SessionProcessorUtil.getModifiedFocusMeteringAction(mSessionProcessor, action); 237 if (modifiedAction == null) { 238 return false; 239 } 240 return mCameraInfo.isFocusMeteringSupported(modifiedAction); 241 } 242 243 /** 244 * Sets if postview is supported or not. 245 */ 246 public void setPostviewSupported(boolean isPostviewSupported) { 247 mIsPostviewSupported = isPostviewSupported; 248 } 249 250 /** 251 * Sets if capture process progress is supported or not. 252 */ 253 public void setCaptureProcessProgressSupported(boolean isCaptureProcessProgressSupported) { 254 mIsCaptureProcessProgressSupported = isCaptureProcessProgressSupported; 255 } 256 257 /** 258 * Returns if postview is supported. 259 */ 260 @Override 261 public boolean isPostviewSupported() { 262 return mIsPostviewSupported; 263 } 264 265 @Override 266 public boolean isCaptureProcessProgressSupported() { 267 return mIsCaptureProcessProgressSupported; 268 } 269 270 @Override 271 public boolean isVideoStabilizationSupported() { 272 if (mSessionProcessor != null) { 273 int[] stabilizationModes = mSessionProcessor.getExtensionAvailableStabilizationModes(); 274 if (stabilizationModes != null) { 275 for (int mode : stabilizationModes) { 276 if (mode == CONTROL_VIDEO_STABILIZATION_MODE_ON) { 277 return true; 278 } 279 } 280 return false; 281 } 282 } 283 return super.isVideoStabilizationSupported(); 284 } 285 286 @Override 287 public boolean isPreviewStabilizationSupported() { 288 if (mSessionProcessor != null) { 289 int[] stabilizationModes = mSessionProcessor.getExtensionAvailableStabilizationModes(); 290 if (stabilizationModes != null) { 291 for (int mode : stabilizationModes) { 292 if (mode == CONTROL_VIDEO_STABILIZATION_MODE_PREVIEW_STABILIZATION) { 293 return true; 294 } 295 } 296 return false; 297 } 298 } 299 return super.isPreviewStabilizationSupported(); 300 } 301 } 302