1 /* 2 * Copyright 2020 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.extensions; 18 19 import static androidx.camera.core.impl.CameraConfig.REQUIRED_RULE_COEXISTING_PREVIEW_AND_IMAGE_CAPTURE; 20 import static androidx.camera.extensions.internal.Camera2ExtensionsUtil.shouldUseCamera2Extensions; 21 22 import android.content.Context; 23 import android.hardware.camera2.CameraManager; 24 import android.os.Build; 25 import android.util.Range; 26 import android.util.Size; 27 28 import androidx.annotation.VisibleForTesting; 29 import androidx.camera.core.CameraFilter; 30 import androidx.camera.core.CameraInfo; 31 import androidx.camera.core.CameraProvider; 32 import androidx.camera.core.CameraSelector; 33 import androidx.camera.core.ImageCapture; 34 import androidx.camera.core.impl.CameraConfigProvider; 35 import androidx.camera.core.impl.ExtendedCameraConfigProviderStore; 36 import androidx.camera.core.impl.Identifier; 37 import androidx.camera.core.impl.SessionProcessor; 38 import androidx.camera.extensions.internal.AdvancedVendorExtender; 39 import androidx.camera.extensions.internal.BasicVendorExtender; 40 import androidx.camera.extensions.internal.Camera2ExtensionsVendorExtender; 41 import androidx.camera.extensions.internal.ClientVersion; 42 import androidx.camera.extensions.internal.ExtensionVersion; 43 import androidx.camera.extensions.internal.ExtensionsUseCaseConfigFactory; 44 import androidx.camera.extensions.internal.VendorExtender; 45 import androidx.camera.extensions.internal.Version; 46 47 import org.jspecify.annotations.NonNull; 48 import org.jspecify.annotations.Nullable; 49 50 import java.util.List; 51 import java.util.Objects; 52 53 /** 54 * A class for querying extensions related information. 55 * 56 * <p>The typical usages include checking whether or not a camera exists that supports an extension 57 * by using {@link #isExtensionAvailable(CameraSelector, int)}. Then after it has been determined 58 * that the extension can be enabled, a 59 * {@link #getExtensionCameraSelectorAndInjectCameraConfig(CameraSelector, int)} call can be used 60 * to get the specified {@link CameraSelector} to bind use cases and enable the extension mode on 61 * the camera. 62 */ 63 final class ExtensionsInfo { 64 private static final String EXTENDED_CAMERA_CONFIG_PROVIDER_ID_PREFIX = ":camera:camera" 65 + "-extensions-"; 66 private static final VendorExtender EMPTY_VENDOR_EXTENDER = new VendorExtender() { 67 }; 68 private final CameraProvider mCameraProvider; 69 private final @Nullable CameraManager mCameraManager; 70 private final boolean mShouldUseCamera2Extensions; 71 private @NonNull VendorExtenderFactory mVendorExtenderFactory; 72 ExtensionsInfo(@onNull CameraProvider cameraProvider, @NonNull Context applicationContext)73 ExtensionsInfo(@NonNull CameraProvider cameraProvider, @NonNull Context applicationContext) { 74 mCameraProvider = cameraProvider; 75 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { 76 mCameraManager = applicationContext.getSystemService(CameraManager.class); 77 } else { 78 mCameraManager = null; 79 } 80 mShouldUseCamera2Extensions = shouldUseCamera2Extensions( 81 mCameraProvider.getConfigImplType()); 82 mVendorExtenderFactory = this::getVendorExtender; 83 } 84 85 /** 86 * Returns a {@link CameraSelector} for the specified extension mode. 87 * 88 * <p>The corresponding extension camera config provider will be injected to the 89 * {@link ExtendedCameraConfigProviderStore} when the function is called. 90 * 91 * @param baseCameraSelector The base {@link CameraSelector} to be applied the extension 92 * related configuration on. 93 * @param mode The target extension mode. 94 * @return a {@link CameraSelector} for the specified Extensions mode. 95 * @throws IllegalArgumentException If no camera can be found to support the specified 96 * extension mode, or the base {@link CameraSelector} has 97 * contained 98 * extension related configuration in it. 99 */ getExtensionCameraSelectorAndInjectCameraConfig( @onNull CameraSelector baseCameraSelector, @ExtensionMode.Mode int mode)100 @NonNull CameraSelector getExtensionCameraSelectorAndInjectCameraConfig( 101 @NonNull CameraSelector baseCameraSelector, 102 @ExtensionMode.Mode int mode) { 103 if (!isExtensionAvailable(baseCameraSelector, mode)) { 104 throw new IllegalArgumentException("No camera can be found to support the specified " 105 + "extensions mode! isExtensionAvailable should be checked first before " 106 + "calling getExtensionEnabledCameraSelector."); 107 } 108 109 // Checks whether there has been Extensions related CameraConfig set in the base 110 // CameraSelector. 111 for (CameraFilter cameraFilter : baseCameraSelector.getCameraFilterSet()) { 112 if (cameraFilter instanceof ExtensionCameraFilter) { 113 throw new IllegalArgumentException( 114 "An extension is already applied to the base CameraSelector."); 115 } 116 } 117 118 // Injects CameraConfigProvider for the extension mode to the 119 // ExtendedCameraConfigProviderStore. 120 injectExtensionCameraConfig(mode); 121 122 CameraSelector.Builder builder = CameraSelector.Builder.fromSelector(baseCameraSelector); 123 124 // Adds the CameraFilter that determines which cameras can support the Extensions mode 125 // to the CameraSelector. 126 builder.addCameraFilter(getFilter(mode)); 127 128 return builder.build(); 129 } 130 131 /** 132 * Returns true if the particular extension mode is available for the specified 133 * {@link CameraSelector}. 134 * 135 * @param baseCameraSelector The base {@link CameraSelector} to find a camera to use. 136 * @param mode The target extension mode to support. 137 */ isExtensionAvailable( @onNull CameraSelector baseCameraSelector, @ExtensionMode.Mode int mode)138 boolean isExtensionAvailable( 139 @NonNull CameraSelector baseCameraSelector, 140 @ExtensionMode.Mode int mode) { 141 CameraSelector.Builder builder = CameraSelector.Builder.fromSelector(baseCameraSelector); 142 builder.addCameraFilter(getFilter(mode)); 143 144 List<CameraInfo> cameraInfos = builder.build().filter( 145 mCameraProvider.getAvailableCameraInfos()); 146 return !cameraInfos.isEmpty(); 147 } 148 149 /** 150 * Returns the estimated capture latency range in milliseconds for the target capture 151 * resolution. 152 * 153 * @param cameraSelector The {@link CameraSelector} to find a camera which supports the 154 * specified extension mode. 155 * @param mode The extension mode to check. 156 * @param resolution The resolution of the {@link ImageCapture} which will be used to 157 * take a picture. If the input value of this parameter is null or 158 * it is not included in the supported output sizes, the maximum 159 * capture output size is used to get the estimated range information. 160 * @return the range of estimated minimal and maximal capture latency in milliseconds. 161 * Returns null if no capture latency info can be provided. 162 * @throws IllegalArgumentException If no camera can be found to support the specified 163 * extension mode. 164 */ getEstimatedCaptureLatencyRange( @onNull CameraSelector cameraSelector, @ExtensionMode.Mode int mode, @Nullable Size resolution)165 @Nullable Range<Long> getEstimatedCaptureLatencyRange( 166 @NonNull CameraSelector cameraSelector, 167 @ExtensionMode.Mode int mode, @Nullable Size resolution) { 168 // Adds the filter to find a CameraInfo of the Camera which supports the specified 169 // extension mode. Checks this first so that the API behavior will be the same no matter 170 // the vendor library is above version 1.2 or not. 171 CameraSelector newCameraSelector = CameraSelector.Builder.fromSelector( 172 cameraSelector).addCameraFilter(getFilter(mode)).build(); 173 174 CameraInfo extensionsCameraInfo; 175 List<CameraInfo> cameraInfos = 176 newCameraSelector.filter(mCameraProvider.getAvailableCameraInfos()); 177 178 if (cameraInfos.isEmpty()) { 179 // Returns null if the specified extension mode is not available. 180 return null; 181 } 182 183 extensionsCameraInfo = cameraInfos.get(0); 184 185 // This API is only supported since version 1.2 186 if (ExtensionVersion.getRuntimeVersion().compareTo(Version.VERSION_1_2) < 0) { 187 return null; 188 } 189 190 try { 191 VendorExtender vendorExtender = mVendorExtenderFactory.createVendorExtender(mode, 192 mShouldUseCamera2Extensions); 193 vendorExtender.init(extensionsCameraInfo); 194 195 return vendorExtender.getEstimatedCaptureLatencyRange(resolution); 196 } catch (NoSuchMethodError e) { 197 return null; 198 } 199 } 200 isImageAnalysisSupported(@onNull CameraSelector cameraSelector, @ExtensionMode.Mode int mode)201 boolean isImageAnalysisSupported(@NonNull CameraSelector cameraSelector, 202 @ExtensionMode.Mode int mode) { 203 CameraSelector newCameraSelector = CameraSelector.Builder.fromSelector( 204 cameraSelector).addCameraFilter(getFilter(mode)).build(); 205 CameraInfo extensionsCameraInfo; 206 List<CameraInfo> cameraInfos = 207 newCameraSelector.filter(mCameraProvider.getAvailableCameraInfos()); 208 209 if (cameraInfos.isEmpty()) { 210 // Returns false if the specified extension mode is not available on this camera. 211 return false; 212 } 213 214 extensionsCameraInfo = cameraInfos.get(0); 215 VendorExtender vendorExtender = mVendorExtenderFactory.createVendorExtender(mode, 216 mShouldUseCamera2Extensions); 217 vendorExtender.init(extensionsCameraInfo); 218 Size[] supportedYuvSizes = vendorExtender.getSupportedYuvAnalysisResolutions(); 219 return supportedYuvSizes != null && supportedYuvSizes.length > 0; 220 } 221 222 @VisibleForTesting setVendorExtenderFactory(@onNull VendorExtenderFactory factory)223 void setVendorExtenderFactory(@NonNull VendorExtenderFactory factory) { 224 mVendorExtenderFactory = factory; 225 } 226 getFilter(@xtensionMode.Mode int mode)227 private CameraFilter getFilter(@ExtensionMode.Mode int mode) { 228 CameraFilter filter; 229 String id = getExtendedCameraConfigProviderId(mode); 230 231 VendorExtender vendorExtender = mVendorExtenderFactory.createVendorExtender(mode, 232 mShouldUseCamera2Extensions); 233 filter = new ExtensionCameraFilter(id, vendorExtender); 234 return filter; 235 } 236 237 /** 238 * Injects {@link CameraConfigProvider} for specified extension mode to the 239 * {@link ExtendedCameraConfigProviderStore}. 240 */ injectExtensionCameraConfig(@xtensionMode.Mode int mode)241 private void injectExtensionCameraConfig(@ExtensionMode.Mode int mode) { 242 Identifier id = Identifier.create(getExtendedCameraConfigProviderId(mode)); 243 244 if (ExtendedCameraConfigProviderStore.getConfigProvider(id) == CameraConfigProvider.EMPTY) { 245 ExtendedCameraConfigProviderStore.addConfig(id, (cameraInfo, context) -> { 246 VendorExtender vendorExtender = mVendorExtenderFactory.createVendorExtender( 247 mode, mShouldUseCamera2Extensions); 248 vendorExtender.init(cameraInfo); 249 250 ExtensionsUseCaseConfigFactory factory = new ExtensionsUseCaseConfigFactory( 251 vendorExtender); 252 253 ExtensionsConfig.Builder builder = new ExtensionsConfig.Builder() 254 .setExtensionMode(mode) 255 .setUseCaseConfigFactory(factory) 256 .setCompatibilityId(id) 257 .setZslDisabled(true) 258 .setPostviewSupported(vendorExtender.isPostviewAvailable()) 259 .setCaptureProcessProgressSupported( 260 vendorExtender.isCaptureProcessProgressAvailable()) 261 .setUseCaseCombinationRequiredRule( 262 REQUIRED_RULE_COEXISTING_PREVIEW_AND_IMAGE_CAPTURE); 263 264 SessionProcessor sessionProcessor = vendorExtender.createSessionProcessor(context); 265 if (sessionProcessor != null) { 266 builder.setSessionProcessor(sessionProcessor); 267 } 268 269 return builder.build(); 270 }); 271 } 272 } 273 274 @NonNull getVendorExtender(@xtensionMode.Mode int mode, boolean useCamera2Extensions)275 VendorExtender getVendorExtender(@ExtensionMode.Mode int mode, boolean useCamera2Extensions) { 276 VendorExtender vendorExtender; 277 if (useCamera2Extensions) { 278 // Always returns Camera2ExtensionsVendorExtender when API level is 31 or above and 279 // configImplType is PIPE. 280 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { 281 vendorExtender = new Camera2ExtensionsVendorExtender(mode, 282 Objects.requireNonNull(mCameraManager)); 283 } else { 284 vendorExtender = EMPTY_VENDOR_EXTENDER; 285 } 286 } else { 287 if (isAdvancedExtenderSupported()) { 288 vendorExtender = new AdvancedVendorExtender(mode); 289 } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { 290 vendorExtender = new BasicVendorExtender(mode); 291 } else { 292 vendorExtender = EMPTY_VENDOR_EXTENDER; 293 } 294 } 295 return vendorExtender; 296 } 297 isAdvancedExtenderSupported()298 private static boolean isAdvancedExtenderSupported() { 299 if (ClientVersion.isMaximumCompatibleVersion(Version.VERSION_1_1) 300 || ExtensionVersion.isMaximumCompatibleVersion(Version.VERSION_1_1)) { 301 return false; 302 } 303 return ExtensionVersion.isAdvancedExtenderSupported(); 304 } 305 getExtendedCameraConfigProviderId(@xtensionMode.Mode int mode)306 private static String getExtendedCameraConfigProviderId(@ExtensionMode.Mode int mode) { 307 String id; 308 309 switch (mode) { 310 case ExtensionMode.BOKEH: 311 id = EXTENDED_CAMERA_CONFIG_PROVIDER_ID_PREFIX + "EXTENSION_MODE_BOKEH"; 312 break; 313 case ExtensionMode.HDR: 314 id = EXTENDED_CAMERA_CONFIG_PROVIDER_ID_PREFIX + "EXTENSION_MODE_HDR"; 315 break; 316 case ExtensionMode.NIGHT: 317 id = EXTENDED_CAMERA_CONFIG_PROVIDER_ID_PREFIX + "EXTENSION_MODE_NIGHT"; 318 break; 319 case ExtensionMode.FACE_RETOUCH: 320 id = EXTENDED_CAMERA_CONFIG_PROVIDER_ID_PREFIX + "EXTENSION_MODE_FACE_RETOUCH"; 321 break; 322 case ExtensionMode.AUTO: 323 id = EXTENDED_CAMERA_CONFIG_PROVIDER_ID_PREFIX + "EXTENSION_MODE_AUTO"; 324 break; 325 case ExtensionMode.NONE: 326 id = EXTENDED_CAMERA_CONFIG_PROVIDER_ID_PREFIX + "EXTENSION_MODE_NONE"; 327 break; 328 default: 329 throw new IllegalArgumentException("Invalid extension mode!"); 330 } 331 return id; 332 } 333 } 334