1 /*
<lambda>null2  * Copyright 2023 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 androidx.annotation.GuardedBy
22 import androidx.camera.camera2.pipe.core.Log.debug
23 import androidx.camera.camera2.pipe.integration.adapter.SessionConfigAdapter
24 import androidx.camera.camera2.pipe.integration.adapter.propagateTo
25 import androidx.camera.camera2.pipe.integration.compat.workaround.AutoFlashAEModeDisabler
26 import androidx.camera.camera2.pipe.integration.config.CameraScope
27 import androidx.camera.core.CameraControl
28 import androidx.camera.core.ImageCapture
29 import androidx.camera.core.UseCase
30 import androidx.camera.core.impl.CaptureConfig
31 import dagger.Binds
32 import dagger.Module
33 import dagger.multibindings.IntoSet
34 import javax.inject.Inject
35 import kotlin.properties.ObservableProperty
36 import kotlin.reflect.KProperty
37 import kotlinx.coroutines.CompletableDeferred
38 import kotlinx.coroutines.Deferred
39 
40 @CameraScope
41 public class State3AControl
42 @Inject
43 constructor(
44     public val cameraProperties: CameraProperties,
45     private val aeModeDisabler: AutoFlashAEModeDisabler,
46 ) : UseCaseCameraControl, UseCaseManager.RunningUseCasesChangeListener {
47     private var _requestControl: UseCaseCameraRequestControl? = null
48     override var requestControl: UseCaseCameraRequestControl?
49         get() = _requestControl
50         set(value) {
51             _requestControl = value
52             value?.let {
53                 val previousSignals =
54                     synchronized(lock) {
55                         updateSignal = null
56                         updateSignals.toList()
57                     }
58 
59                 invalidate() // Always apply the settings to the camera.
60 
61                 synchronized(lock) { updateSignal }?.propagateToAll(previousSignals)
62                     ?: run { for (signals in previousSignals) signals.complete(Unit) }
63             }
64         }
65 
66     override fun onRunningUseCasesChanged(runningUseCases: Set<UseCase>) {
67         if (runningUseCases.isNotEmpty()) {
68             runningUseCases.updateTemplate()
69         }
70     }
71 
72     private fun Deferred<Unit>.propagateToAll(previousSignals: List<CompletableDeferred<Unit>>) {
73         for (previousSignal in previousSignals) {
74             propagateTo(previousSignal)
75         }
76     }
77 
78     private val lock = Any()
79     private val invalidateLock = Any()
80 
81     @GuardedBy("lock") private val updateSignals = mutableSetOf<CompletableDeferred<Unit>>()
82 
83     @GuardedBy("lock")
84     public var updateSignal: Deferred<Unit>? = null
85         private set
86 
87     public var flashMode: Int by updateOnPropertyChange(DEFAULT_FLASH_MODE)
88     public var template: Int by updateOnPropertyChange(DEFAULT_REQUEST_TEMPLATE)
89     public var tryExternalFlashAeMode: Boolean by updateOnPropertyChange(false)
90 
91     /**
92      * The [CaptureRequest.CONTROL_AE_MODE] that is set to camera if supported.
93      *
94      * If null, a value based on other settings is calculated and available via
95      * [getFinalPreferredAeMode]. If not supported, [getSupportedAeMode] is used to find the next
96      * best option.
97      */
98     public var preferredAeMode: Int? by updateOnPropertyChange(null)
99     public var preferredFocusMode: Int? by updateOnPropertyChange(null)
100 
101     override fun reset() {
102         synchronized(lock) { updateSignals.toList() }.cancelAll()
103         tryExternalFlashAeMode = false
104         preferredAeMode = null
105         preferredFocusMode = null
106         flashMode = DEFAULT_FLASH_MODE
107         template = DEFAULT_REQUEST_TEMPLATE
108     }
109 
110     private fun <T> updateOnPropertyChange(initialValue: T) =
111         object : ObservableProperty<T>(initialValue) {
112             override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
113                 synchronized(invalidateLock) { super.setValue(thisRef, property, value) }
114             }
115 
116             override fun afterChange(property: KProperty<*>, oldValue: T, newValue: T) {
117                 if (newValue != oldValue) {
118                     invalidate()
119                 }
120             }
121         }
122 
123     /**
124      * Returns the AE mode that is finally set to camera based on all other settings and camera
125      * capabilities.
126      */
127     public fun getFinalSupportedAeMode(): Int =
128         cameraProperties.metadata.getSupportedAeMode(getFinalPreferredAeMode())
129 
130     /**
131      * Returns the AE mode that is finally set to camera based on all other settings.
132      *
133      * Note that this may not be supported via the camera and should be sanitized with
134      * [getSupportedAeMode].
135      */
136     private fun getFinalPreferredAeMode(): Int {
137         var preferAeMode =
138             preferredAeMode
139                 ?: when (flashMode) {
140                     ImageCapture.FLASH_MODE_OFF -> CaptureRequest.CONTROL_AE_MODE_ON
141                     ImageCapture.FLASH_MODE_ON -> CaptureRequest.CONTROL_AE_MODE_ON_ALWAYS_FLASH
142                     ImageCapture.FLASH_MODE_AUTO ->
143                         aeModeDisabler.getCorrectedAeMode(
144                             CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH
145                         )
146                     else -> CaptureRequest.CONTROL_AE_MODE_ON
147                 }
148 
149         // Overwrite AE mode to ON_EXTERNAL_FLASH only if required and explicitly supported
150         if (tryExternalFlashAeMode) {
151             val isSupported = cameraProperties.metadata.isExternalFlashAeModeSupported()
152             debug {
153                 "State3AControl.invalidate: trying external flash AE mode" +
154                     ", supported = $isSupported"
155             }
156             if (isSupported) {
157                 preferAeMode = CaptureRequest.CONTROL_AE_MODE_ON_EXTERNAL_FLASH
158             }
159         }
160 
161         debug { "State3AControl.getFinalPreferredAeMode: preferAeMode = $preferAeMode" }
162 
163         return preferAeMode
164     }
165 
166     public fun invalidate() {
167         // TODO(b/276779600): Refactor and move the setting of these parameter to
168         //  CameraGraph.Config(requiredParameters = mapOf(....)).
169         synchronized(invalidateLock) {
170                 val preferAeMode = getFinalPreferredAeMode()
171                 val preferAfMode = preferredFocusMode ?: getDefaultAfMode()
172 
173                 val parameters: MutableMap<CaptureRequest.Key<*>, Any> =
174                     mutableMapOf(
175                         CaptureRequest.CONTROL_AE_MODE to
176                             cameraProperties.metadata.getSupportedAeMode(preferAeMode),
177                         CaptureRequest.CONTROL_AF_MODE to
178                             cameraProperties.metadata.getSupportedAfMode(preferAfMode),
179                         CaptureRequest.CONTROL_AWB_MODE to
180                             cameraProperties.metadata.getSupportedAwbMode(
181                                 CaptureRequest.CONTROL_AWB_MODE_AUTO
182                             )
183                     )
184 
185                 requestControl?.setParametersAsync(values = parameters)
186             }
187             ?.apply {
188                 toCompletableDeferred().also { signal ->
189                     synchronized(lock) {
190                         updateSignals.add(signal)
191                         updateSignal = signal
192                         signal.invokeOnCompletion {
193                             synchronized(lock) { updateSignals.remove(signal) }
194                         }
195                     }
196                 }
197             } ?: run { synchronized(lock) { updateSignal = CompletableDeferred(Unit) } }
198     }
199 
200     private fun getDefaultAfMode(): Int =
201         when (template) {
202             CameraDevice.TEMPLATE_RECORD -> CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_VIDEO
203             CameraDevice.TEMPLATE_PREVIEW -> CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE
204             else -> CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE
205         }
206 
207     private fun Collection<UseCase>.updateTemplate() {
208         SessionConfigAdapter(this).getValidSessionConfigOrNull()?.let {
209             val templateType = it.repeatingCaptureConfig.templateType
210             template =
211                 if (templateType != CaptureConfig.TEMPLATE_TYPE_NONE) {
212                     templateType
213                 } else {
214                     DEFAULT_REQUEST_TEMPLATE
215                 }
216         }
217     }
218 
219     private fun <T> Deferred<T>.toCompletableDeferred() =
220         CompletableDeferred<T>().also { propagateTo(it) }
221 
222     private fun <T> Collection<CompletableDeferred<T>>.cancelAll() = forEach {
223         it.completeExceptionally(CameraControl.OperationCanceledException("Camera is not active."))
224     }
225 
226     @Module
227     public abstract class Bindings {
228         @Binds
229         @IntoSet
230         public abstract fun provideControls(state3AControl: State3AControl): UseCaseCameraControl
231     }
232 }
233