1 /*
2  * Copyright 2021 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.pipe.integration.interop
18 
19 import androidx.annotation.RestrictTo
20 import androidx.annotation.VisibleForTesting
21 import androidx.camera.camera2.pipe.integration.adapter.CameraControlAdapter
22 import androidx.camera.camera2.pipe.integration.adapter.asListenableFuture
23 import androidx.camera.camera2.pipe.integration.compat.Camera2CameraControlCompat
24 import androidx.camera.camera2.pipe.integration.impl.ComboRequestListener
25 import androidx.camera.camera2.pipe.integration.impl.UseCaseCameraControl
26 import androidx.camera.camera2.pipe.integration.impl.UseCaseCameraRequestControl
27 import androidx.camera.camera2.pipe.integration.impl.UseCaseThreads
28 import androidx.camera.core.CameraControl
29 import androidx.camera.core.impl.CameraControlInternal
30 import androidx.camera.core.impl.utils.futures.Futures
31 import androidx.core.util.Preconditions
32 import com.google.common.util.concurrent.ListenableFuture
33 
34 /**
35  * An class that provides ability to interoperate with the [android.hardware.camera2] APIs.
36  *
37  * Camera2 specific controls, like capture request options, can be applied through this class. A
38  * Camera2CameraControl can be created from a general [CameraControl] which is associated to a
39  * camera. Then the controls will affect all use cases that are using that camera.
40  *
41  * If any option applied by Camera2CameraControl conflicts with the options required by CameraX
42  * internally. The options from Camera2CameraControl will override, which may result in unexpected
43  * behavior depends on the options being applied.
44  */
45 @SuppressWarnings("HiddenSuperclass")
46 @ExperimentalCamera2Interop
47 public class Camera2CameraControl
48 private constructor(
49     private val compat: Camera2CameraControlCompat,
50     private val threads: UseCaseThreads,
51     @get:VisibleForTesting internal val requestListener: ComboRequestListener,
52 ) : UseCaseCameraControl {
53 
54     private var _useCaseCameraRequestControl: UseCaseCameraRequestControl? = null
55     override var requestControl: UseCaseCameraRequestControl?
56         @RestrictTo(RestrictTo.Scope.LIBRARY) get() = _useCaseCameraRequestControl
57         @RestrictTo(RestrictTo.Scope.LIBRARY)
58         set(value) {
59             _useCaseCameraRequestControl = value
<lambda>null60             _useCaseCameraRequestControl?.also {
61                 requestListener.removeListener(compat)
62                 requestListener.addListener(compat, threads.sequentialExecutor)
63                 compat.applyAsync(it, false)
64             }
65         }
66 
67     @RestrictTo(RestrictTo.Scope.LIBRARY)
resetnull68     override fun reset() {
69         // Clear the current task, but don't clear the CaptureRequestOptions. Camera2CameraControl
70         // will store the CaptureRequestOptions while use case is detached.
71         compat.cancelCurrentTask()
72         requestListener.removeListener(compat)
73     }
74 
75     /**
76      * Sets a [CaptureRequestOptions] and updates the session with the options it contains.
77      *
78      * This will first clear all options that have already been set, then apply the new options.
79      *
80      * Any values which are in conflict with values already set by CameraX, such as by
81      * [androidx.camera.core.CameraControl], will overwrite the existing values. The values will be
82      * submitted with every repeating and single capture requests issued by CameraX, which may
83      * result in unexpected behavior depending on the values being applied.
84      *
85      * @param bundle The [CaptureRequestOptions] which will be set.
86      * @return a [ListenableFuture] which completes when the repeating
87      *   [android.hardware.camera2.CaptureResult] shows the options have be submitted completely.
88      *   The future fails with [CameraControl.OperationCanceledException] if newer options are set
89      *   or camera is closed before the current request completes. Cancelling the ListenableFuture
90      *   is a no-op.
91      */
92     @SuppressWarnings("AsyncSuffixFuture")
setCaptureRequestOptionsnull93     public fun setCaptureRequestOptions(bundle: CaptureRequestOptions): ListenableFuture<Void?> {
94         compat.clearRequestOption()
95         compat.addRequestOption(bundle)
96         return updateAsync("setCaptureRequestOptions")
97     }
98 
99     /**
100      * Adds a [CaptureRequestOptions] updates the session with the options it contains.
101      *
102      * The options will be merged with the existing options. If one option is set with a different
103      * value, it will overwrite the existing value.
104      *
105      * Any values which are in conflict with values already set by CameraX, such as by
106      * [androidx.camera.core.CameraControl], will overwrite the existing values. The values will be
107      * submitted with every repeating and single capture requests issued by CameraX, which may
108      * result in unexpected behavior depends on the values being applied.
109      *
110      * @param bundle The [CaptureRequestOptions] which will be set.
111      * @return a [ListenableFuture] which completes when the repeating
112      *   [android.hardware.camera2.CaptureResult] shows the options have be submitted completely.
113      *   The future fails with [CameraControl.OperationCanceledException] if newer options are set
114      *   or camera is closed before the current request completes.
115      */
116     @SuppressWarnings("AsyncSuffixFuture")
addCaptureRequestOptionsnull117     public fun addCaptureRequestOptions(bundle: CaptureRequestOptions): ListenableFuture<Void?> {
118         compat.addRequestOption(bundle)
119         return updateAsync("addCaptureRequestOptions")
120     }
121 
122     /**
123      * Gets all the capture request options that is currently applied by the [Camera2CameraControl].
124      *
125      * It doesn't include the capture request options applied by the
126      * [android.hardware.camera2.CameraDevice] templates or by CameraX.
127      *
128      * @return The [CaptureRequestOptions].
129      */
getCaptureRequestOptionsnull130     public fun getCaptureRequestOptions(): CaptureRequestOptions = compat.getRequestOption()
131 
132     /**
133      * Clears all capture request options that is currently applied by the [Camera2CameraControl].
134      *
135      * @return a [ListenableFuture] which completes when the repeating
136      *   [android.hardware.camera2.CaptureResult] shows the options have be submitted completely.
137      *   The future fails with [CameraControl.OperationCanceledException] if newer options are set
138      *   or camera is closed before the current request completes.
139      */
140     @SuppressWarnings("AsyncSuffixFuture")
141     public fun clearCaptureRequestOptions(): ListenableFuture<Void?> {
142         compat.clearRequestOption()
143         return updateAsync("clearCaptureRequestOptions")
144     }
145 
updateAsyncnull146     private fun updateAsync(tag: String): ListenableFuture<Void?> =
147         Futures.nonCancellationPropagating(
148             compat.applyAsync(requestControl).asListenableFuture(tag)
149         )
150 
151     public companion object {
152 
153         /**
154          * Gets the [Camera2CameraControl] from a [CameraControl].
155          *
156          * The [CameraControl] is still usable after a [Camera2CameraControl] is obtained from it.
157          * Note that the [Camera2CameraControl] has higher priority than the [CameraControl]. For
158          * example, if [android.hardware.camera2.CaptureRequest.FLASH_MODE] is set through the
159          * [Camera2CameraControl]. All [CameraControl] features that required
160          * [android.hardware.camera2.CaptureRequest.FLASH_MODE] internally like torch may not work
161          * properly.
162          *
163          * @param cameraControl The [CameraControl] to get from.
164          * @return The camera control with Camera2 implementation.
165          * @throws IllegalArgumentException if the camera control does not contain the camera2
166          *   information (e.g., if CameraX was not initialized with a
167          *   [androidx.camera.camera2.pipe.integration.CameraPipeConfig]).
168          */
169         @JvmStatic
170         public fun from(cameraControl: CameraControl): Camera2CameraControl {
171             var cameraControlImpl = (cameraControl as CameraControlInternal).implementation
172             Preconditions.checkArgument(
173                 cameraControlImpl is CameraControlAdapter,
174                 "CameraControl doesn't contain Camera2 implementation."
175             )
176             return (cameraControlImpl as CameraControlAdapter).camera2cameraControl
177         }
178 
179         /** This is the workaround to prevent constructor from being added to public API. */
180         @RestrictTo(RestrictTo.Scope.LIBRARY)
181         @JvmStatic
182         public fun create(
183             compat: Camera2CameraControlCompat,
184             threads: UseCaseThreads,
185             requestListener: ComboRequestListener,
186         ): Camera2CameraControl {
187             return Camera2CameraControl(compat, threads, requestListener)
188         }
189     }
190 }
191