1 /*
<lambda>null2 * 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.impl
18
19 import android.hardware.camera2.CameraDevice
20 import android.hardware.camera2.CaptureRequest
21 import android.hardware.camera2.params.MeteringRectangle
22 import androidx.annotation.AnyThread
23 import androidx.camera.camera2.pipe.AeMode
24 import androidx.camera.camera2.pipe.CameraGraph
25 import androidx.camera.camera2.pipe.CameraGraph.Constants3A.METERING_REGIONS_DEFAULT
26 import androidx.camera.camera2.pipe.Lock3ABehavior
27 import androidx.camera.camera2.pipe.Request
28 import androidx.camera.camera2.pipe.RequestTemplate
29 import androidx.camera.camera2.pipe.Result3A
30 import androidx.camera.camera2.pipe.StreamId
31 import androidx.camera.camera2.pipe.core.Log.debug
32 import androidx.camera.camera2.pipe.integration.config.UseCaseCameraScope
33 import androidx.camera.camera2.pipe.integration.config.UseCaseGraphConfig
34 import androidx.camera.core.ImageCapture
35 import androidx.camera.core.ImageCaptureException
36 import androidx.camera.core.impl.CaptureConfig
37 import androidx.camera.core.impl.CaptureConfig.TEMPLATE_TYPE_NONE
38 import androidx.camera.core.impl.Config
39 import androidx.camera.core.impl.MutableTagBundle
40 import androidx.camera.core.impl.SessionConfig
41 import androidx.camera.core.impl.TagBundle
42 import dagger.Binds
43 import dagger.Module
44 import javax.inject.Inject
45 import kotlinx.coroutines.CancellationException
46 import kotlinx.coroutines.CompletableDeferred
47 import kotlinx.coroutines.Deferred
48
49 internal const val DEFAULT_REQUEST_TEMPLATE = CameraDevice.TEMPLATE_PREVIEW
50
51 /**
52 * Provides methods to update the configuration and parameters of the camera. It also stores the
53 * repeating request parameters associated with the configured [UseCaseCamera]. When parameters are
54 * updated, it triggers changes in the [UseCaseCameraState].
55 *
56 * Parameters can be stored and managed according to different configuration types. Each type can be
57 * modified or overridden independently without affecting other types.
58 *
59 * This class should be used as the entry point for submitting requests to the [UseCaseCameraScope]
60 * layer. This ensures that thread confinement are properly applied at a single place for the whole
61 * [UseCaseCameraScope] and reduces concurrency issues.
62 */
63 @JvmDefaultWithCompatibility
64 public interface UseCaseCameraRequestControl {
65 /** Defines the types or categories of configuration parameters. */
66 public enum class Type {
67 /** Parameters related to the overall session configuration. */
68 SESSION_CONFIG,
69 /** General, default parameters. */
70 DEFAULT,
71 /** Parameters specifically for interoperability with Camera2. */
72 CAMERA2_CAMERA_CONTROL
73 }
74
75 // Repeating Request Parameters
76 /**
77 * Asynchronously sets or updates parameters for the repeating capture request.
78 *
79 * New values will overwrite any existing parameters with the same key for the given [type]. If
80 * no [type] is specified, it defaults to [Type.DEFAULT].
81 *
82 * @param type The category of parameters being set (default: [Type.DEFAULT]).
83 * @param values A map of [CaptureRequest.Key] to their new values.
84 * @param optionPriority The priority for resolving conflicts if the same parameter is set
85 * multiple times.
86 * @return A [Deferred] object representing the asynchronous operation.
87 */
88 @AnyThread
89 public fun setParametersAsync(
90 type: Type = Type.DEFAULT,
91 values: Map<CaptureRequest.Key<*>, Any> = emptyMap(),
92 optionPriority: Config.OptionPriority = defaultOptionPriority,
93 ): Deferred<Unit>
94
95 /**
96 * Asynchronously updates the repeating request with a new configuration.
97 *
98 * This method replaces any existing configuration, tags, and listeners associated with the
99 * specified [type]. The repeating request is then rebuilt by merging all configurations, tags,
100 * and listeners from all defined types.
101 *
102 * @param type The category of the configuration being updated (e.g., SESSION_CONFIG, DEFAULT).
103 * @param config The new configuration values to apply. If null, the existing configuration for
104 * this type is cleared.
105 * @param tags Optional tags to append to the repeating request, similar to
106 * [CaptureRequest.Builder.setTag].
107 * @param streams The specific streams to update. If empty, all previously specified streams are
108 * updated. The update only proceeds if at least one valid stream is specified.
109 * @param template The [RequestTemplate] to use for the requests. If null, the previously
110 * specified template is used.
111 * @param listeners Listeners to receive capture results.
112 * @param sessionConfig Optional [SessionConfig] to update if applicable to the configuration
113 * type.
114 * @return A [Deferred] representing the asynchronous update operation.
115 */
116 @AnyThread
117 public fun setConfigAsync(
118 type: Type,
119 config: Config? = null,
120 tags: Map<String, Any> = emptyMap(),
121 streams: Set<StreamId>? = null,
122 template: RequestTemplate? = null,
123 listeners: Set<Request.Listener> = emptySet(),
124 sessionConfig: SessionConfig? = null,
125 ): Deferred<Unit>
126
127 // 3A
128 /**
129 * Asynchronously sets the torch (flashlight) to ON state.
130 *
131 * @return A [Deferred] representing the asynchronous operation and its result ([Result3A]).
132 */
133 @AnyThread public fun setTorchOnAsync(): Deferred<Result3A>
134
135 /**
136 * Asynchronously sets the torch (flashlight) state to OFF state.
137 *
138 * @param aeMode The [AeMode] to set while setting the torch value. See
139 * [CameraGraph.Session.setTorchOff] for details.
140 * @return A [Deferred] representing the asynchronous operation and its result ([Result3A]).
141 */
142 @AnyThread public fun setTorchOffAsync(aeMode: AeMode): Deferred<Result3A>
143
144 /**
145 * Asynchronously starts a 3A (Auto Exposure, Auto Focus, Auto White Balance) operation with the
146 * specified regions and locking behaviors.
147 *
148 * @param aeRegions The auto-exposure regions.
149 * @param afRegions The auto-focus regions.
150 * @param awbRegions The auto-white balance regions.
151 * @param aeLockBehavior The behavior for locking auto-exposure.
152 * @param afLockBehavior The behavior for locking auto-focus.
153 * @param awbLockBehavior The behavior for locking auto-white balance.
154 * @param afTriggerStartAeMode The AE mode to use when triggering AF.
155 * @param timeLimitNs The time limit for the 3A operation in nanoseconds. Defaults to
156 * [CameraGraph.Constants3A.DEFAULT_TIME_LIMIT_NS].
157 * @return A [Deferred] representing the asynchronous operation and its result ([Result3A]).
158 */
159 @AnyThread
160 public fun startFocusAndMeteringAsync(
161 aeRegions: List<MeteringRectangle>? = null,
162 afRegions: List<MeteringRectangle>? = null,
163 awbRegions: List<MeteringRectangle>? = null,
164 aeLockBehavior: Lock3ABehavior? = null,
165 afLockBehavior: Lock3ABehavior? = null,
166 awbLockBehavior: Lock3ABehavior? = null,
167 afTriggerStartAeMode: AeMode? = null,
168 timeLimitNs: Long = CameraGraph.Constants3A.DEFAULT_TIME_LIMIT_NS,
169 ): Deferred<Result3A>
170
171 /**
172 * Asynchronously cancels any ongoing focus and metering operations.
173 *
174 * @return A [Deferred] representing the asynchronous operation and its result ([Result3A]).
175 */
176 @AnyThread public fun cancelFocusAndMeteringAsync(): Deferred<Result3A>
177
178 // Capture
179 /**
180 * Asynchronously issues a single capture request.
181 *
182 * @param captureSequence A list of [CaptureConfig] objects defining the capture settings.
183 * @param captureMode The capture mode (from [ImageCapture.CaptureMode]).
184 * @param flashType The flash type (from [ImageCapture.FlashType]).
185 * @param flashMode The flash mode (from [ImageCapture.FlashMode]).
186 * @return A list of [Deferred] objects, one for each capture in the sequence.
187 */
188 @AnyThread
189 public fun issueSingleCaptureAsync(
190 captureSequence: List<CaptureConfig>,
191 @ImageCapture.CaptureMode captureMode: Int,
192 @ImageCapture.FlashType flashType: Int,
193 @ImageCapture.FlashMode flashMode: Int,
194 ): List<Deferred<Void?>>
195
196 /**
197 * Updates the 3A regions and applies to the repeating request.
198 *
199 * Note that camera-pipe may invalidate the CameraGraph and update the repeating request
200 * parameters for this operations.
201 *
202 * @see [CameraGraph.Session.update3A]
203 */
204 @AnyThread
205 public fun update3aRegions(
206 aeRegions: List<MeteringRectangle>? = null,
207 afRegions: List<MeteringRectangle>? = null,
208 awbRegions: List<MeteringRectangle>? = null,
209 ): Deferred<Result3A>
210
211 /**
212 * Waits for any ongoing surface setup to be completed and returns a boolean value to indicate
213 * if a successful setup exists.
214 *
215 * @see UseCaseSurfaceManager.awaitSetupCompletion
216 */
217 public suspend fun awaitSurfaceSetup(): Boolean
218
219 public fun close()
220 }
221
222 @UseCaseCameraScope
223 public class UseCaseCameraRequestControlImpl
224 @Inject
225 constructor(
226 private val capturePipeline: CapturePipeline,
227 private val state: UseCaseCameraState,
228 private val useCaseGraphConfig: UseCaseGraphConfig,
229 private val useCaseSurfaceManager: UseCaseSurfaceManager,
230 private val threads: UseCaseThreads,
231 ) : UseCaseCameraRequestControl {
232 private val graph = useCaseGraphConfig.graph
233
234 @Volatile private var closed = false
235
236 private data class InfoBundle(
237 val options: Camera2ImplConfig.Builder = Camera2ImplConfig.Builder(),
238 val tags: MutableMap<String, Any> = mutableMapOf(),
239 val listeners: MutableSet<Request.Listener> = mutableSetOf(),
240 var template: RequestTemplate? = null,
241 )
242
243 private val infoBundleMap = mutableMapOf<UseCaseCameraRequestControl.Type, InfoBundle>()
244
setParametersAsyncnull245 override fun setParametersAsync(
246 type: UseCaseCameraRequestControl.Type,
247 values: Map<CaptureRequest.Key<*>, Any>,
248 optionPriority: Config.OptionPriority,
249 ): Deferred<Unit> =
250 runIfNotClosed {
251 threads.confineDeferred {
252 debug {
253 "UseCaseCameraRequestControlImpl#setParametersAsync: [$type] values = $values" +
254 ", optionPriority = $optionPriority"
255 }
256 infoBundleMap
257 .getOrPut(type) { InfoBundle() }
258 .options
259 .addAllCaptureRequestOptionsWithPriority(values, optionPriority)
260 infoBundleMap.merge().updateCameraStateAsync()
261 }
262 } ?: canceledResult
263
setConfigAsyncnull264 override fun setConfigAsync(
265 type: UseCaseCameraRequestControl.Type,
266 config: Config?,
267 tags: Map<String, Any>,
268 streams: Set<StreamId>?,
269 template: RequestTemplate?,
270 listeners: Set<Request.Listener>,
271 sessionConfig: SessionConfig?,
272 ): Deferred<Unit> =
273 runIfNotClosed {
274 threads.confineDeferred {
275 debug {
276 "UseCaseCameraRequestControlImpl#setConfigAsync:" +
277 " [$type] config params = ${config?.toParameters()}"
278 }
279 infoBundleMap[type] =
280 InfoBundle(
281 Camera2ImplConfig.Builder().apply { config?.let { insertAllOptions(it) } },
282 tags.toMutableMap(),
283 listeners.toMutableSet(),
284 template,
285 )
286 infoBundleMap
287 .merge()
288 .updateCameraStateAsync(
289 streams = streams,
290 sessionConfig = sessionConfig,
291 )
292 }
293 } ?: canceledResult
294
setTorchOnAsyncnull295 override fun setTorchOnAsync(): Deferred<Result3A> =
296 runIfNotClosed {
297 threads.confineDeferredSuspend {
298 debug { "UseCaseCameraRequestControlImpl#setTorchOnAsync" }
299 useGraphSessionOrFailed { it.setTorchOn() }
300 }
301 } ?: submitFailedResult
302
setTorchOffAsyncnull303 override fun setTorchOffAsync(aeMode: AeMode): Deferred<Result3A> =
304 runIfNotClosed {
305 threads.confineDeferredSuspend {
306 debug { "UseCaseCameraRequestControlImpl#setTorchOffAsync" }
307 useGraphSessionOrFailed {
308 it.setTorchOff(
309 aeMode = aeMode,
310 )
311 }
312 }
313 } ?: submitFailedResult
314
startFocusAndMeteringAsyncnull315 override fun startFocusAndMeteringAsync(
316 aeRegions: List<MeteringRectangle>?,
317 afRegions: List<MeteringRectangle>?,
318 awbRegions: List<MeteringRectangle>?,
319 aeLockBehavior: Lock3ABehavior?,
320 afLockBehavior: Lock3ABehavior?,
321 awbLockBehavior: Lock3ABehavior?,
322 afTriggerStartAeMode: AeMode?,
323 timeLimitNs: Long,
324 ): Deferred<Result3A> =
325 runIfNotClosed {
326 threads.confineDeferredSuspend {
327 debug { "UseCaseCameraRequestControlImpl#startFocusAndMeteringAsync" }
328 useGraphSessionOrFailed {
329 it.lock3A(
330 aeRegions = aeRegions,
331 afRegions = afRegions,
332 awbRegions = awbRegions,
333 aeLockBehavior = aeLockBehavior,
334 afLockBehavior = afLockBehavior,
335 awbLockBehavior = awbLockBehavior,
336 afTriggerStartAeMode = afTriggerStartAeMode,
337 convergedTimeLimitNs = timeLimitNs,
338 lockedTimeLimitNs = timeLimitNs
339 )
340 }
341 }
342 } ?: submitFailedResult
343
cancelFocusAndMeteringAsyncnull344 override fun cancelFocusAndMeteringAsync(): Deferred<Result3A> =
345 runIfNotClosed {
346 threads.confineDeferredSuspend {
347 debug { "UseCaseCameraRequestControlImpl#cancelFocusAndMeteringAsync" }
348
349 useGraphSessionOrFailed { it.unlock3A(ae = true, af = true, awb = true) }.await()
350
351 useGraphSessionOrFailed {
352 it.update3A(
353 aeRegions = METERING_REGIONS_DEFAULT.asList(),
354 afRegions = METERING_REGIONS_DEFAULT.asList(),
355 awbRegions = METERING_REGIONS_DEFAULT.asList()
356 )
357 }
358 }
359 } ?: submitFailedResult
360
issueSingleCaptureAsyncnull361 override fun issueSingleCaptureAsync(
362 captureSequence: List<CaptureConfig>,
363 @ImageCapture.CaptureMode captureMode: Int,
364 @ImageCapture.FlashType flashType: Int,
365 @ImageCapture.FlashMode flashMode: Int,
366 ): List<Deferred<Void?>> =
367 runIfNotClosed {
368 threads.confineDeferredListSuspend(captureSequence.size) {
369 debug { "UseCaseCameraRequestControlImpl#issueSingleCaptureAsync" }
370
371 if (captureSequence.hasInvalidSurface()) {
372 failedResults(
373 captureSequence.size,
374 "Capture request failed due to invalid surface"
375 )
376 }
377
378 infoBundleMap.merge().let { infoBundle ->
379 debug {
380 "UseCaseCameraRequestControl: Submitting still captures to capture pipeline"
381 }
382 capturePipeline.submitStillCaptures(
383 configs = captureSequence,
384 requestTemplate = infoBundle.template!!,
385 sessionConfigOptions = infoBundle.options.build(),
386 captureMode = captureMode,
387 flashType = flashType,
388 flashMode = flashMode,
389 )
390 }
391 }
392 }
393 ?: failedResults(
394 captureSequence.size,
395 "Capture request is cancelled on closed CameraGraph"
396 )
397
update3aRegionsnull398 override fun update3aRegions(
399 aeRegions: List<MeteringRectangle>?,
400 afRegions: List<MeteringRectangle>?,
401 awbRegions: List<MeteringRectangle>?
402 ): Deferred<Result3A> =
403 runIfNotClosed {
404 threads.confineDeferredSuspend {
405 debug { "UseCaseCameraRequestControlImpl#update3aRegions" }
406 useGraphSessionOrFailed {
407 it.update3A(
408 aeRegions = aeRegions ?: METERING_REGIONS_DEFAULT.asList(),
409 afRegions = afRegions ?: METERING_REGIONS_DEFAULT.asList(),
410 awbRegions = awbRegions ?: METERING_REGIONS_DEFAULT.asList()
411 )
412 }
413 }
414 } ?: submitFailedResult
415
awaitSurfaceSetupnull416 override suspend fun awaitSurfaceSetup(): Boolean = useCaseSurfaceManager.awaitSetupCompletion()
417
418 override fun close() {
419 closed = true
420 debug { "UseCaseCameraRequestControl: closed" }
421 }
422
failedResultsnull423 private fun failedResults(count: Int, message: String): List<Deferred<Void?>> =
424 List(count) {
425 CompletableDeferred<Void>().apply {
426 completeExceptionally(
427 ImageCaptureException(ImageCapture.ERROR_CAPTURE_FAILED, message, null)
428 )
429 }
430 }
431
hasInvalidSurfacenull432 private fun List<CaptureConfig>.hasInvalidSurface(): Boolean {
433 forEach { captureConfig ->
434 if (captureConfig.surfaces.isEmpty()) {
435 return true
436 }
437 captureConfig.surfaces.forEach {
438 if (useCaseGraphConfig.surfaceToStreamMap[it] == null) {
439 return true
440 }
441 }
442 }
443 return false
444 }
445
446 /**
447 * The merge order is the same as the [UseCaseCameraRequestControl.Type] declaration order.
448 *
449 * Option merge: The earlier merged option in [Config.OptionPriority.OPTIONAL] could be
450 * overridden by later merged options. Tag merge: If there is the same tagKey but tagValue is
451 * different, the later merge would override the earlier one. Listener merge: merge the
452 * listeners into a set.
453 */
mergenull454 private fun Map<UseCaseCameraRequestControl.Type, InfoBundle>.merge(): InfoBundle =
455 InfoBundle(template = RequestTemplate(DEFAULT_REQUEST_TEMPLATE)).apply {
456 UseCaseCameraRequestControl.Type.values().forEach { type ->
457 getOrElse(type) { InfoBundle() }
458 .let { infoBundleInType ->
459 options.insertAllOptions(infoBundleInType.options.mutableConfig)
460 tags.putAll(infoBundleInType.tags)
461 listeners.addAll(infoBundleInType.listeners)
462 infoBundleInType.template?.let { template = it }
463 }
464 }
465 }
466
toTagBundlenull467 private fun InfoBundle.toTagBundle(): TagBundle =
468 MutableTagBundle.create().also { tagBundle ->
469 tags.forEach { (tagKey, tagValue) -> tagBundle.putTag(tagKey, tagValue) }
470 }
471
updateCameraStateAsyncnull472 private fun InfoBundle.updateCameraStateAsync(
473 streams: Set<StreamId>? = null,
474 sessionConfig: SessionConfig? = null,
475 ): Deferred<Unit> =
476 runIfNotClosed {
477 capturePipeline.template =
478 if (template != null && template!!.value != TEMPLATE_TYPE_NONE) {
479 template!!.value
480 } else {
481 DEFAULT_REQUEST_TEMPLATE
482 }
483
484 state.updateAsync(
485 parameters = options.build().toParameters(),
486 appendParameters = false,
487 internalParameters = mapOf(CAMERAX_TAG_BUNDLE to toTagBundle()),
488 appendInternalParameters = false,
489 streams = streams,
490 template = template,
491 listeners = listeners,
492 sessionConfig = sessionConfig,
493 )
494 } ?: canceledResult
495
runIfNotClosednull496 private inline fun <R> runIfNotClosed(block: () -> R): R? {
497 return if (!closed) block() else null
498 }
499
useGraphSessionOrFailednull500 private suspend inline fun useGraphSessionOrFailed(
501 crossinline block: suspend (CameraGraph.Session) -> Deferred<Result3A>
502 ): Deferred<Result3A> =
503 try {
504 graph.acquireSession().use { block(it) }
505 } catch (e: CancellationException) {
<lambda>null506 debug(e) { "Cannot acquire the CameraGraph.Session" }
507 submitFailedResult
508 }
509
510 @Module
511 public abstract class Bindings {
512 @UseCaseCameraScope
513 @Binds
provideRequestControlsnull514 public abstract fun provideRequestControls(
515 requestControl: UseCaseCameraRequestControlImpl
516 ): UseCaseCameraRequestControl
517 }
518
519 public companion object {
520 private val submitFailedResult =
521 CompletableDeferred(Result3A(Result3A.Status.SUBMIT_FAILED))
522 private val canceledResult = CompletableDeferred<Unit>().apply { cancel() }
523 }
524 }
525
toMapnull526 public fun TagBundle.toMap(): Map<String, Any> =
527 mutableMapOf<String, Any>().also {
528 listKeys().forEach { tagKey -> it[tagKey] = getTag(tagKey) as Any }
529 }
530