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