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.core.impl;
18 
19 import static androidx.camera.core.impl.UseCaseConfig.OPTION_PREVIEW_STABILIZATION_MODE;
20 import static androidx.camera.core.impl.UseCaseConfig.OPTION_VIDEO_STABILIZATION_MODE;
21 
22 import android.hardware.camera2.CameraCaptureSession;
23 import android.hardware.camera2.CameraDevice;
24 import android.hardware.camera2.CaptureRequest;
25 import android.util.Range;
26 import android.view.Surface;
27 
28 import androidx.camera.core.impl.stabilization.StabilizationMode;
29 
30 import org.jspecify.annotations.NonNull;
31 import org.jspecify.annotations.Nullable;
32 
33 import java.util.ArrayList;
34 import java.util.Collection;
35 import java.util.Collections;
36 import java.util.HashSet;
37 import java.util.List;
38 import java.util.Objects;
39 import java.util.Set;
40 
41 /**
42  * Configurations needed for a capture request.
43  *
44  * <p>The CaptureConfig contains all the {@link android.hardware.camera2} parameters that are
45  * required to issue a {@link CaptureRequest}.
46  */
47 public final class CaptureConfig {
48     /** Indicates template type is not set. */
49     public static final int TEMPLATE_TYPE_NONE = -1;
50 
51     /**
52      * Request that the implementation rotate the image.
53      *
54      * <p> Currently only applicable for {@link androidx.camera.core.ImageProxy} which are of
55      * JPEG format.
56      *
57      * Option: camerax.core.rotation
58      */
59     public static final Config.Option<Integer> OPTION_ROTATION =
60             Config.Option.create("camerax.core.captureConfig.rotation", int.class);
61 
62     /**
63      * Sets the compression quality of the captured JPEG image.
64      *
65      * See {@link CaptureRequest#JPEG_QUALITY}.
66      *
67      * Option: camerax.core.captureConfig.jpegQuality
68      */
69     public static final Config.Option<Integer> OPTION_JPEG_QUALITY =
70             Config.Option.create("camerax.core.captureConfig.jpegQuality", Integer.class);
71 
72     /**
73      * Option: camerax.core.camera.resolvedFrameRate
74      *
75      * <p> The frame rate that is resolved for all use cases based on supported surface/stream spec
76      * combinations.
77      */
78     private static final Config.Option<Range<Integer>> OPTION_RESOLVED_FRAME_RATE =
79             Config.Option.create("camerax.core.captureConfig.resolvedFrameRate", Range.class);
80 
81     /** Key to get/set the CaptureConfig ID from the TagBundle */
82     public static final String CAPTURE_CONFIG_ID_TAG_KEY = "CAPTURE_CONFIG_ID_KEY";
83 
84     public static final int DEFAULT_ID = -1;
85 
86     /** The set of {@link Surface} that data from the camera will be put into. */
87     final List<DeferrableSurface> mSurfaces;
88 
89     final Config mImplementationOptions;
90 
91     /**
92      * The templates used for configuring a {@link CaptureRequest}. This must match the constants
93      * defined by {@link CameraDevice}
94      */
95     final int mTemplateType;
96 
97     final boolean mPostviewEnabled;
98 
99     /** The camera capture callback for a {@link CameraCaptureSession}. */
100     final List<CameraCaptureCallback> mCameraCaptureCallbacks;
101 
102     /** True if this capture request needs a repeating surface */
103     private final boolean mUseRepeatingSurface;
104 
105     /** The tag collection for associating capture result with capture request. */
106     private final @NonNull TagBundle mTagBundle;
107 
108     /**
109      * The camera capture result for reprocessing capture request.
110      */
111     private final @Nullable CameraCaptureResult mCameraCaptureResult;
112 
113     /**
114      * Private constructor for a CaptureConfig.
115      *
116      * <p>In practice, the {@link CaptureConfig.Builder} will be used to construct a CaptureConfig.
117      *
118      * @param surfaces               The set of {@link Surface} where data will be put into.
119      * @param implementationOptions  The generic parameters to be passed to the
120      *                               {@link CameraInternal} class.
121      * @param templateType           The template for parameters of the CaptureRequest. This
122      *                               must match the
123      *                               constants defined by {@link CameraDevice}.
124      * @param cameraCaptureCallbacks All camera capture callbacks.
125      * @param cameraCaptureResult     The {@link CameraCaptureResult} for reprocessing capture
126      *                               request.
127      */
CaptureConfig( List<DeferrableSurface> surfaces, Config implementationOptions, int templateType, boolean postviewEnabled, List<CameraCaptureCallback> cameraCaptureCallbacks, boolean useRepeatingSurface, @NonNull TagBundle tagBundle, @Nullable CameraCaptureResult cameraCaptureResult)128     CaptureConfig(
129             List<DeferrableSurface> surfaces,
130             Config implementationOptions,
131             int templateType,
132             boolean postviewEnabled,
133             List<CameraCaptureCallback> cameraCaptureCallbacks,
134             boolean useRepeatingSurface,
135             @NonNull TagBundle tagBundle,
136             @Nullable CameraCaptureResult cameraCaptureResult) {
137         mSurfaces = surfaces;
138         mImplementationOptions = implementationOptions;
139         mTemplateType = templateType;
140         mCameraCaptureCallbacks = Collections.unmodifiableList(cameraCaptureCallbacks);
141         mUseRepeatingSurface = useRepeatingSurface;
142         mTagBundle = tagBundle;
143         mCameraCaptureResult = cameraCaptureResult;
144         mPostviewEnabled = postviewEnabled;
145     }
146 
147     /** Returns an instance of a capture configuration with minimal configurations. */
defaultEmptyCaptureConfig()148     public static @NonNull CaptureConfig defaultEmptyCaptureConfig() {
149         return new CaptureConfig.Builder().build();
150     }
151 
152     /**
153      * Returns an instance of {@link CameraCaptureResult} for reprocessing capture request.
154      *
155      * @return {@link CameraCaptureResult}.
156      */
getCameraCaptureResult()157     public @Nullable CameraCaptureResult getCameraCaptureResult() {
158         return mCameraCaptureResult;
159     }
160 
161     /** Get all the surfaces that the request will write data to. */
getSurfaces()162     public @NonNull List<DeferrableSurface> getSurfaces() {
163         return Collections.unmodifiableList(mSurfaces);
164     }
165 
getImplementationOptions()166     public @NonNull Config getImplementationOptions() {
167         return mImplementationOptions;
168     }
169 
170     /**
171      * Gets the template type.
172      *
173      * <p>If not set, returns {@link #TEMPLATE_TYPE_NONE}.
174      */
getTemplateType()175     public int getTemplateType() {
176         return mTemplateType;
177     }
178 
179     /**
180      * Returns the ID of the {@link CaptureConfig} that identifies which {@link CaptureConfig} is
181      * triggering the {@link CameraCaptureCallback} callback methods upon its submission.
182      *
183      * <p>The ID will be passed in every methods in {@link CameraCaptureCallback}. Callers have
184      * to set the ID explicitly otherwise it returns {@link #DEFAULT_ID} by default.
185      */
getId()186     public int getId() {
187         Object id = mTagBundle.getTag(CAPTURE_CONFIG_ID_TAG_KEY);
188         if (id == null) {
189             return DEFAULT_ID;
190         }
191         return (int) id;
192     }
193 
getExpectedFrameRateRange()194     public @NonNull Range<Integer> getExpectedFrameRateRange() {
195         return Objects.requireNonNull(
196                 mImplementationOptions.retrieveOption(OPTION_RESOLVED_FRAME_RATE,
197                         StreamSpec.FRAME_RATE_RANGE_UNSPECIFIED));
198     }
199 
200     @StabilizationMode.Mode
getPreviewStabilizationMode()201     public int getPreviewStabilizationMode() {
202         return Objects.requireNonNull(mImplementationOptions.retrieveOption(
203                 UseCaseConfig.OPTION_PREVIEW_STABILIZATION_MODE, StabilizationMode.UNSPECIFIED));
204     }
205 
206     @StabilizationMode.Mode
getVideoStabilizationMode()207     public int getVideoStabilizationMode() {
208         return Objects.requireNonNull(
209                 mImplementationOptions.retrieveOption(OPTION_VIDEO_STABILIZATION_MODE,
210                         StabilizationMode.UNSPECIFIED));
211     }
212 
isPostviewEnabled()213     public boolean isPostviewEnabled() {
214         return mPostviewEnabled;
215     }
216 
isUseRepeatingSurface()217     public boolean isUseRepeatingSurface() {
218         return mUseRepeatingSurface;
219     }
220 
221     /** Obtains all registered {@link CameraCaptureCallback} callbacks. */
getCameraCaptureCallbacks()222     public @NonNull List<CameraCaptureCallback> getCameraCaptureCallbacks() {
223         return mCameraCaptureCallbacks;
224     }
225 
getTagBundle()226     public @NonNull TagBundle getTagBundle() {
227         return mTagBundle;
228     }
229 
230     /**
231      * Interface for unpacking a configuration into a CaptureConfig.Builder
232      */
233     public interface OptionUnpacker {
234 
235         /**
236          * Apply the options from the config onto the builder
237          *
238          * @param config  the set of options to apply
239          * @param builder the builder on which to apply the options
240          */
unpack(@onNull UseCaseConfig<?> config, CaptureConfig.@NonNull Builder builder)241         void unpack(@NonNull UseCaseConfig<?> config, CaptureConfig.@NonNull Builder builder);
242     }
243 
244     /**
245      * Builder for easy modification/rebuilding of a {@link CaptureConfig}.
246      */
247     public static final class Builder {
248         private final Set<DeferrableSurface> mSurfaces = new HashSet<>();
249         private MutableConfig mImplementationOptions = MutableOptionsBundle.create();
250         private int mTemplateType = TEMPLATE_TYPE_NONE;
251         private boolean mPostviewEnabled = false;
252         private List<CameraCaptureCallback> mCameraCaptureCallbacks = new ArrayList<>();
253         private boolean mUseRepeatingSurface = false;
254         private MutableTagBundle mMutableTagBundle = MutableTagBundle.create();
255         private @Nullable CameraCaptureResult mCameraCaptureResult;
256 
Builder()257         public Builder() {
258         }
259 
Builder(CaptureConfig base)260         private Builder(CaptureConfig base) {
261             mSurfaces.addAll(base.mSurfaces);
262             mImplementationOptions = MutableOptionsBundle.from(base.mImplementationOptions);
263             mTemplateType = base.mTemplateType;
264             mCameraCaptureCallbacks.addAll(base.getCameraCaptureCallbacks());
265             mUseRepeatingSurface = base.isUseRepeatingSurface();
266             mMutableTagBundle = MutableTagBundle.from(base.getTagBundle());
267             mPostviewEnabled = base.mPostviewEnabled;
268         }
269 
270         /**
271          * Creates a {@link Builder} from a {@link UseCaseConfig}.
272          *
273          * <p>Populates the builder with all the properties defined in the base configuration.
274          */
createFrom(@onNull UseCaseConfig<?> config)275         public static @NonNull Builder createFrom(@NonNull UseCaseConfig<?> config) {
276             OptionUnpacker unpacker = config.getCaptureOptionUnpacker(null);
277             if (unpacker == null) {
278                 throw new IllegalStateException(
279                         "Implementation is missing option unpacker for "
280                                 + config.getTargetName(config.toString()));
281             }
282 
283             Builder builder = new Builder();
284 
285             // Unpack the configuration into this builder
286             unpacker.unpack(config, builder);
287             return builder;
288         }
289 
290         /** Create a {@link Builder} from a {@link CaptureConfig} */
from(@onNull CaptureConfig base)291         public static @NonNull Builder from(@NonNull CaptureConfig base) {
292             return new Builder(base);
293         }
294 
295         /**
296          * Set the {@link CameraCaptureResult} for reprocessable capture request.
297          *
298          * @param cameraCaptureResult {@link CameraCaptureResult}.
299          */
setCameraCaptureResult(@onNull CameraCaptureResult cameraCaptureResult)300         public void setCameraCaptureResult(@NonNull CameraCaptureResult cameraCaptureResult) {
301             mCameraCaptureResult = cameraCaptureResult;
302         }
303 
getTemplateType()304         public int getTemplateType() {
305             return mTemplateType;
306         }
307 
getExpectedFrameRateRange()308         public @Nullable Range<Integer> getExpectedFrameRateRange() {
309             return mImplementationOptions.retrieveOption(OPTION_RESOLVED_FRAME_RATE,
310                     StreamSpec.FRAME_RATE_RANGE_UNSPECIFIED);
311         }
312 
313         /**
314          * Set the template characteristics of the CaptureConfig.
315          *
316          * @param templateType Template constant that must match those defined by {@link
317          *                     CameraDevice}
318          */
setTemplateType(int templateType)319         public void setTemplateType(int templateType) {
320             mTemplateType = templateType;
321         }
322 
323         /**
324          * Set the expected frame rate range of the CaptureConfig.
325          * @param expectedFrameRateRange The frame rate range calculated from the UseCases for
326          * {@link CameraDevice}
327          */
setExpectedFrameRateRange(@onNull Range<Integer> expectedFrameRateRange)328         public void setExpectedFrameRateRange(@NonNull Range<Integer> expectedFrameRateRange) {
329             addImplementationOption(OPTION_RESOLVED_FRAME_RATE, expectedFrameRateRange);
330         }
331 
332         /**
333          * Set the preview stabilization mode of the CaptureConfig.
334          * @param mode {@link StabilizationMode}
335          */
setPreviewStabilization(@tabilizationMode.Mode int mode)336         public void setPreviewStabilization(@StabilizationMode.Mode int mode) {
337             if (mode != StabilizationMode.UNSPECIFIED) {
338                 addImplementationOption(OPTION_PREVIEW_STABILIZATION_MODE, mode);
339             }
340         }
341 
342         /**
343          * Set the video stabilization mode of the CaptureConfig.
344          * @param mode {@link StabilizationMode}
345          */
setVideoStabilization(@tabilizationMode.Mode int mode)346         public void setVideoStabilization(@StabilizationMode.Mode int mode) {
347             if (mode != StabilizationMode.UNSPECIFIED) {
348                 addImplementationOption(OPTION_VIDEO_STABILIZATION_MODE, mode);
349             }
350         }
351 
setPostviewEnabled(boolean postviewEnabled)352         public void setPostviewEnabled(boolean postviewEnabled) {
353             mPostviewEnabled = postviewEnabled;
354         }
355 
356         /**
357          * Adds a {@link CameraCaptureCallback} callback.
358          */
addCameraCaptureCallback(@onNull CameraCaptureCallback cameraCaptureCallback)359         public void addCameraCaptureCallback(@NonNull CameraCaptureCallback cameraCaptureCallback) {
360             if (mCameraCaptureCallbacks.contains(cameraCaptureCallback)) {
361                 return;
362             }
363             mCameraCaptureCallbacks.add(cameraCaptureCallback);
364         }
365 
366         /**
367          * Adds all {@link CameraCaptureCallback} callbacks.
368          */
addAllCameraCaptureCallbacks( @onNull Collection<CameraCaptureCallback> cameraCaptureCallbacks)369         public void addAllCameraCaptureCallbacks(
370                 @NonNull Collection<CameraCaptureCallback> cameraCaptureCallbacks) {
371             for (CameraCaptureCallback c : cameraCaptureCallbacks) {
372                 addCameraCaptureCallback(c);
373             }
374         }
375 
376         /**
377          * Removes a previously added {@link CameraCaptureCallback} callback.
378          * @param cameraCaptureCallback The callback to remove.
379          * @return {@code true} if the callback was successfully removed. {@code false} if the
380          * callback wasn't present in this builder.
381          */
removeCameraCaptureCallback( @onNull CameraCaptureCallback cameraCaptureCallback)382         public boolean removeCameraCaptureCallback(
383                 @NonNull CameraCaptureCallback cameraCaptureCallback) {
384             return mCameraCaptureCallbacks.remove(cameraCaptureCallback);
385         }
386 
387         /** Add a surface that the request will write data to. */
addSurface(@onNull DeferrableSurface surface)388         public void addSurface(@NonNull DeferrableSurface surface) {
389             mSurfaces.add(surface);
390         }
391 
392         /** Remove a surface that the request will write data to. */
removeSurface(@onNull DeferrableSurface surface)393         public void removeSurface(@NonNull DeferrableSurface surface) {
394             mSurfaces.remove(surface);
395         }
396 
397         /** Remove all the surfaces that the request will write data to. */
clearSurfaces()398         public void clearSurfaces() {
399             mSurfaces.clear();
400         }
401 
402         /** Gets the surfaces attached to the request. */
getSurfaces()403         public @NonNull Set<DeferrableSurface> getSurfaces() {
404             return mSurfaces;
405         }
406 
setImplementationOptions(@onNull Config config)407         public void setImplementationOptions(@NonNull Config config) {
408             mImplementationOptions = MutableOptionsBundle.from(config);
409         }
410 
411         /** Add a set of implementation specific options to the request. */
412         @SuppressWarnings("unchecked")
addImplementationOptions(@onNull Config config)413         public void addImplementationOptions(@NonNull Config config) {
414             for (Config.Option<?> option : config.listOptions()) {
415                 @SuppressWarnings("unchecked") // Options/values are being copied directly
416                         Config.Option<Object> objectOpt = (Config.Option<Object>) option;
417 
418                 Object existValue = mImplementationOptions.retrieveOption(objectOpt, null);
419                 Object newValue = config.retrieveOption(objectOpt);
420                 if (existValue instanceof MultiValueSet) {
421                     ((MultiValueSet) existValue).addAll(((MultiValueSet) newValue).getAllItems());
422                 } else {
423                     if (newValue instanceof MultiValueSet) {
424                         newValue = ((MultiValueSet) newValue).clone();
425                     }
426                     mImplementationOptions.insertOption(objectOpt,
427                             config.getOptionPriority(option), newValue);
428                 }
429             }
430         }
431 
432         /** Add a single implementation option to the request. */
addImplementationOption(Config.@onNull Option<T> option, @NonNull T value)433         public <T> void addImplementationOption(Config.@NonNull Option<T> option,
434                 @NonNull T value) {
435             mImplementationOptions.insertOption(option, value);
436         }
437 
getImplementationOptions()438         public @NonNull Config getImplementationOptions() {
439             return mImplementationOptions;
440         }
441 
isUseRepeatingSurface()442         public boolean isUseRepeatingSurface() {
443             return mUseRepeatingSurface;
444         }
445 
setUseRepeatingSurface(boolean useRepeatingSurface)446         public void setUseRepeatingSurface(boolean useRepeatingSurface) {
447             mUseRepeatingSurface = useRepeatingSurface;
448         }
449 
450         /** Gets a tag's value by a key. */
getTag(@onNull String key)451         public @Nullable Object getTag(@NonNull String key) {
452             return mMutableTagBundle.getTag(key);
453         }
454 
455         /**
456          * Sets a tag with a key to CaptureConfig.
457          */
addTag(@onNull String key, @NonNull Object tag)458         public void addTag(@NonNull String key, @NonNull Object tag) {
459             mMutableTagBundle.putTag(key, tag);
460         }
461 
462         /**
463          * Sets the ID of the {@link CaptureConfig} that helps identify which
464          * {@link CaptureConfig} is triggering the {@link CameraCaptureCallback} callback methods
465          * upon its submission.
466          *
467          * <p>The ID will be passed in every methods in {@link CameraCaptureCallback}. To ensure
468          * it uniquely identifies the {@link CaptureConfig}, set a unique ID for every
469          * CaptureConfig.
470          */
setId(int id)471         public void setId(int id) {
472             mMutableTagBundle.putTag(CAPTURE_CONFIG_ID_TAG_KEY, id);
473         }
474         /**
475          * Adds a TagBundle to CaptureConfig.
476          */
addAllTags(@onNull TagBundle bundle)477         public void addAllTags(@NonNull TagBundle bundle) {
478             mMutableTagBundle.addTagBundle(bundle);
479         }
480 
481         /**
482          * Builds an instance of a CaptureConfig that has all the combined parameters of the
483          * CaptureConfig that have been added to the Builder.
484          */
build()485         public @NonNull CaptureConfig build() {
486             return new CaptureConfig(
487                     new ArrayList<>(mSurfaces),
488                     OptionsBundle.from(mImplementationOptions),
489                     mTemplateType,
490                     mPostviewEnabled,
491                     new ArrayList<>(mCameraCaptureCallbacks),
492                     mUseRepeatingSurface,
493                     TagBundle.from(mMutableTagBundle),
494                     mCameraCaptureResult);
495         }
496     }
497 }
498