1 /*
2  * Copyright 2022 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.testing
18 
19 import android.view.Surface
20 import androidx.camera.camera2.pipe.CameraContext
21 import androidx.camera.camera2.pipe.CameraController
22 import androidx.camera.camera2.pipe.CameraGraph
23 import androidx.camera.camera2.pipe.CameraGraphId
24 import androidx.camera.camera2.pipe.CameraId
25 import androidx.camera.camera2.pipe.GraphState.GraphStateError
26 import androidx.camera.camera2.pipe.StreamGraph
27 import androidx.camera.camera2.pipe.StreamId
28 import androidx.camera.camera2.pipe.graph.GraphListener
29 import androidx.camera.camera2.pipe.graph.GraphRequestProcessor
30 
31 /**
32  * The CameraControllerSimulator is a [CameraController] implementation designed to simulate actions
33  * and reactions to an underlying camera.
34  *
35  * Most interactions with a [CameraController] are in the form of "action -> reaction" where the
36  * reaction must be simulated, and is useful for testing nuanced behavior with how a real camera
37  * controller may behave in different circumstances or edge cases. As an example, invoking [start]
38  * must be paired with [simulateCameraStarted] in most situations for a [CameraGraph] to be able to
39  * actively submit requests. This mirrors the underlying behavior of an actual Camera, which may
40  * take time to configure and become ready.
41  */
42 public class CameraControllerSimulator(
43     cameraContext: CameraContext,
44     private val graphId: CameraGraphId,
45     private val graphConfig: CameraGraph.Config,
46     private val graphListener: GraphListener
47 ) : CameraController {
48     override val cameraId: CameraId
49         get() = graphConfig.camera
50 
51     override val cameraGraphId: CameraGraphId
52         get() = graphId
53 
54     override var isForeground: Boolean = true
55 
56     private val lock = Any()
57     private var currentSurfaceMap: Map<StreamId, Surface> = emptyMap()
58     private var currentGraphRequestProcessor: GraphRequestProcessor? = null
59 
60     private var _closed = false
61     public var closed: Boolean
62         get() = _closed
63         private set(value) {
64             _closed = value
65         }
66 
67     private var _started = false
68     public var started: Boolean
69         get() = _started
70         private set(value) {
71             _started = value
72         }
73 
74     public var currentCaptureSequenceProcessor: FakeCaptureSequenceProcessor? = null
75         private set
76 
77     public var outputLatencySet: StreamGraph.OutputLatency? = null
78         private set
79 
80     public var streamGraph: StreamGraph? = null
81 
82     public val simulatedCaptureLatency: Long = 5L
83     public val simulatedProcessingLatency: Long = 10L
84 
85     init {
<lambda>null86         check(cameraContext.cameraBackends.allIds.isNotEmpty()) {
87             "Backends provided by cameraContext.cameraBackends cannot be empty"
88         }
89         val cameraBackendId = graphConfig.cameraBackendId
90         if (cameraBackendId != null) {
<lambda>null91             check(cameraContext.cameraBackends.allIds.contains(cameraBackendId)) {
92                 "Backends provided by cameraContext do not contain $cameraBackendId which was " +
93                     "requested by $graphConfig"
94             }
95         }
96     }
97 
simulateCameraStartednull98     public fun simulateCameraStarted() {
99         synchronized(lock) {
100             check(!closed) {
101                 "Attempted to invoke simulateStarted after the CameraController was closed."
102             }
103 
104             val captureSequenceProcessor =
105                 FakeCaptureSequenceProcessor(graphConfig.camera, graphConfig.defaultTemplate)
106             val graphRequestProcessor = GraphRequestProcessor.from(captureSequenceProcessor)
107             captureSequenceProcessor.surfaceMap = currentSurfaceMap
108             currentCaptureSequenceProcessor = captureSequenceProcessor
109             currentGraphRequestProcessor = graphRequestProcessor
110 
111             graphListener.onGraphStarted(graphRequestProcessor)
112         }
113     }
114 
simulateCameraStoppednull115     public fun simulateCameraStopped() {
116         synchronized(lock) {
117             check(!closed) {
118                 "Attempted to invoke simulateCameraStopped after the CameraController was closed."
119             }
120             val captureSequenceProcessor = currentCaptureSequenceProcessor
121             val graphRequestProcessor = currentGraphRequestProcessor
122 
123             currentCaptureSequenceProcessor = null
124             currentGraphRequestProcessor = null
125 
126             if (captureSequenceProcessor != null && graphRequestProcessor != null) {
127                 graphListener.onGraphStopped(graphRequestProcessor)
128             }
129         }
130     }
131 
simulateCameraModifiednull132     public fun simulateCameraModified() {
133         synchronized(lock) {
134             val captureSequenceProcessor = currentCaptureSequenceProcessor
135             val graphRequestProcessor = currentGraphRequestProcessor
136 
137             currentCaptureSequenceProcessor = null
138             currentGraphRequestProcessor = null
139 
140             if (captureSequenceProcessor != null && graphRequestProcessor != null) {
141                 graphListener.onGraphStopped(graphRequestProcessor)
142             }
143         }
144     }
145 
simulateCameraErrornull146     public fun simulateCameraError(graphStateError: GraphStateError) {
147         synchronized(lock) {
148             check(!closed) {
149                 "Attempted to invoke simulateCameraError after the CameraController was closed."
150             }
151             graphListener.onGraphError(graphStateError)
152         }
153     }
154 
simulateOutputLatencynull155     public fun simulateOutputLatency() {
156         outputLatencySet =
157             StreamGraph.OutputLatency(simulatedCaptureLatency, simulatedProcessingLatency)
158     }
159 
startnull160     override fun start() {
161         synchronized(lock) {
162             check(!closed) { "Attempted to invoke start after close." }
163             started = true
164         }
165     }
166 
stopnull167     override fun stop() {
168         synchronized(lock) {
169             check(!closed) { "Attempted to invoke stop after close." }
170             started = false
171         }
172     }
173 
closenull174     override fun close() {
175         synchronized(lock) {
176             closed = true
177             started = false
178         }
179     }
180 
updateSurfaceMapnull181     override fun updateSurfaceMap(surfaceMap: Map<StreamId, Surface>) {
182         streamGraph?.streamIds?.containsAll(surfaceMap.keys).let { check(it == true) }
183 
184         synchronized(lock) {
185             currentSurfaceMap = surfaceMap
186 
187             val captureSequenceProcessor = currentCaptureSequenceProcessor
188             val graphRequestProcessor = currentGraphRequestProcessor
189             if (captureSequenceProcessor != null && graphRequestProcessor != null) {
190                 captureSequenceProcessor.surfaceMap = surfaceMap
191                 graphListener.onGraphModified(graphRequestProcessor)
192             }
193         }
194     }
195 
getOutputLatencynull196     override fun getOutputLatency(streamId: StreamId?): StreamGraph.OutputLatency? {
197         return outputLatencySet
198     }
199 }
200