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