1 /*
2 * Copyright 2019 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.integration.antelope.cameracontrollers
18
19 import android.content.Context
20 import android.hardware.camera2.CameraAccessException
21 import android.hardware.camera2.CameraDevice
22 import android.hardware.camera2.CameraManager
23 import android.hardware.camera2.CameraMetadata
24 import android.hardware.camera2.CaptureRequest
25 import androidx.camera.integration.antelope.CameraParams
26 import androidx.camera.integration.antelope.FocusMode
27 import androidx.camera.integration.antelope.MainActivity
28 import androidx.camera.integration.antelope.MainActivity.Companion.logd
29 import androidx.camera.integration.antelope.PrefHelper
30 import androidx.camera.integration.antelope.TestConfig
31 import androidx.camera.integration.antelope.TestType
32 import androidx.camera.integration.antelope.getOrientation
33 import androidx.camera.integration.antelope.setAutoFlash
34 import java.lang.Thread.sleep
35 import java.util.Arrays
36
37 /** State of the camera during an image capture - */
38 internal enum class CameraState {
39 UNINITIALIZED,
40 PREVIEW_RUNNING,
41 WAITING_FOCUS_LOCK,
42 WAITING_EXPOSURE_LOCK,
43 IMAGE_REQUESTED
44 }
45
46 /**
47 * Opens the camera using the Camera 2 API and starts the open counter. The open call will complete
48 * in the DeviceStateCallback asynchronously. For switch tests, the camera id will be swizzling so
49 * the original camera id is saved.
50 */
camera2OpenCameranull51 fun camera2OpenCamera(activity: MainActivity, params: CameraParams?, testConfig: TestConfig) {
52 if (null == params) return
53
54 val manager = activity.getSystemService(Context.CAMERA_SERVICE) as CameraManager
55 try {
56 // TODO make the switch test methodology more robust and handle physical cameras
57 if (
58 (testConfig.currentRunningTest == TestType.SWITCH_CAMERA) ||
59 (testConfig.currentRunningTest == TestType.MULTI_SWITCH)
60 ) {
61 testConfig.switchTestRealCameraId = params.id // Save the original camera ID
62 params.id = testConfig.switchTestCurrentCamera
63 }
64
65 // Might be a new test, update callbacks to match the test config
66 params.camera2DeviceStateCallback = Camera2DeviceStateCallback(params, activity, testConfig)
67 params.camera2CaptureSessionCallback =
68 Camera2CaptureSessionCallback(activity, params, testConfig)
69
70 params.timer.openStart = System.currentTimeMillis()
71 logd(
72 "openCamera: " +
73 params.id +
74 " running test: " +
75 testConfig.currentRunningTest.toString()
76 )
77
78 manager.openCamera(params.id, params.camera2DeviceStateCallback!!, params.backgroundHandler)
79 } catch (e: CameraAccessException) {
80 logd("openCamera CameraAccessException: " + params.id)
81 e.printStackTrace()
82 } catch (e: SecurityException) {
83 logd("openCamera SecurityException: " + params.id)
84 e.printStackTrace()
85 }
86 }
87
88 /** Setup the camera preview session and output surface. */
createCameraPreviewSessionnull89 fun createCameraPreviewSession(
90 activity: MainActivity,
91 params: CameraParams,
92 testConfig: TestConfig
93 ) {
94
95 logd("In createCameraPreviewSession.")
96 if (!params.isOpen) {
97 return
98 }
99
100 try {
101 val surface = params.previewSurfaceView?.holder?.surface
102 if (null == surface) return
103
104 val imageSurface = params.imageReader?.surface
105 if (null == imageSurface) return
106
107 params.captureRequestBuilder =
108 params.device?.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW)
109
110 if (params.previewSurfaceView?.holder?.surface != null)
111 params.captureRequestBuilder?.removeTarget(params.previewSurfaceView?.holder?.surface!!)
112 params.captureRequestBuilder?.addTarget(surface)
113
114 @Suppress("DEPRECATION")
115 params.device?.createCaptureSession(
116 Arrays.asList(surface, imageSurface),
117 Camera2PreviewSessionStateCallback(activity, params, testConfig),
118 null
119 )
120 } catch (e: CameraAccessException) {
121 MainActivity.logd("createCameraPreviewSession CameraAccessException: " + e.message)
122 e.printStackTrace()
123 } catch (e: IllegalStateException) {
124 MainActivity.logd("createCameraPreviewSession IllegalStateException: " + e.message)
125 e.printStackTrace()
126 }
127 }
128
129 /**
130 * Set up timers for a still capture. The preview stream is allowed to run here in order to fill the
131 * pipeline with images to simulate more realistic camera conditions.
132 */
initializeStillCapturenull133 fun initializeStillCapture(activity: MainActivity, params: CameraParams, testConfig: TestConfig) {
134 logd("TakePicture: capture start.")
135
136 if (!params.isOpen) {
137 return
138 }
139
140 if (params.timer.isFirstPhoto) {
141 params.timer.isFirstPhoto = false
142 }
143
144 logd(
145 "Camera2 initializeStillCapture: 1st photo in a multi-photo test. " +
146 "Pausing for " +
147 PrefHelper.getPreviewBuffer(activity) +
148 "ms to let preview run."
149 )
150 params.timer.previewFillStart = System.currentTimeMillis()
151 sleep(PrefHelper.getPreviewBuffer(activity))
152 params.timer.previewFillEnd = System.currentTimeMillis()
153
154 params.timer.captureStart = System.currentTimeMillis()
155 params.timer.autofocusStart = System.currentTimeMillis()
156 lockFocus(activity, params, testConfig)
157 }
158
159 /** Initiate the auto-focus routine if required. */
lockFocusnull160 fun lockFocus(activity: MainActivity, params: CameraParams, testConfig: TestConfig) {
161 logd("In lockFocus.")
162 if (!params.isOpen) {
163 return
164 }
165
166 try {
167 if (null != params.device) {
168 setAutoFlash(params, params.captureRequestBuilder)
169 if (params.imageReader?.surface != null)
170 params.captureRequestBuilder?.addTarget(params.imageReader?.surface!!)
171
172 // If this lens can focus, we need to start a focus search and wait for focus lock
173 if (params.hasAF && FocusMode.AUTO == testConfig.focusMode) {
174 logd("In lockFocus. About to request focus lock and call capture.")
175
176 params.captureRequestBuilder?.set(
177 CaptureRequest.CONTROL_AF_MODE,
178 CaptureRequest.CONTROL_AF_MODE_AUTO
179 )
180 params.captureRequestBuilder?.set(
181 CaptureRequest.CONTROL_AF_TRIGGER,
182 CameraMetadata.CONTROL_AF_TRIGGER_CANCEL
183 )
184 params.camera2CaptureSession?.capture(
185 params.captureRequestBuilder?.build()!!,
186 params.camera2CaptureSessionCallback,
187 params.backgroundHandler
188 )
189
190 params.captureRequestBuilder?.set(
191 CaptureRequest.CONTROL_AF_MODE,
192 CaptureRequest.CONTROL_AF_MODE_AUTO
193 )
194 params.captureRequestBuilder?.set(
195 CaptureRequest.CONTROL_AF_TRIGGER,
196 CameraMetadata.CONTROL_AF_TRIGGER_START
197 )
198
199 params.state = CameraState.WAITING_FOCUS_LOCK
200
201 params.autoFocusStuckCounter = 0
202 params.camera2CaptureSession?.capture(
203 params.captureRequestBuilder?.build()!!,
204 params.camera2CaptureSessionCallback,
205 params.backgroundHandler
206 )
207 } else {
208 // If no auto-focus requested, go ahead to the still capture routine
209 logd("In lockFocus. Fixed focus or continuous focus, calling captureStillPicture.")
210 params.state = CameraState.IMAGE_REQUESTED
211 captureStillPicture(activity, params, testConfig)
212 }
213 }
214 } catch (e: CameraAccessException) {
215 e.printStackTrace()
216 }
217 }
218
219 /** Request pre-capture auto-exposure (AE) metering */
runPrecaptureSequencenull220 fun runPrecaptureSequence(params: CameraParams) {
221 if (!params.isOpen) {
222 return
223 }
224
225 try {
226 if (null != params.device) {
227 setAutoFlash(params, params.captureRequestBuilder)
228 params.captureRequestBuilder?.set(
229 CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER,
230 CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_START
231 )
232
233 params.state = CameraState.WAITING_EXPOSURE_LOCK
234 params.camera2CaptureSession?.capture(
235 params.captureRequestBuilder?.build()!!,
236 params.camera2CaptureSessionCallback,
237 params.backgroundHandler
238 )
239 }
240 } catch (e: CameraAccessException) {
241 e.printStackTrace()
242 }
243 }
244
245 /** Make a still capture request. At this point, AF and AE should be converged or unnecessary. */
246 @Suppress("DEPRECATION") /* defaultDisplay */
captureStillPicturenull247 fun captureStillPicture(activity: MainActivity, params: CameraParams, testConfig: TestConfig) {
248 if (!params.isOpen) {
249 return
250 }
251
252 try {
253 logd("In captureStillPicture. Current test: " + testConfig.currentRunningTest.toString())
254
255 if (null != params.device) {
256 params.timer.autofocusEnd = System.currentTimeMillis()
257
258 params.captureRequestBuilder =
259 params.device?.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE)
260
261 if (params.imageReader?.surface != null)
262 params.captureRequestBuilder?.addTarget(params.imageReader?.surface!!)
263
264 when (testConfig.focusMode) {
265 FocusMode.CONTINUOUS -> {
266 params.captureRequestBuilder?.set(
267 CaptureRequest.CONTROL_AF_MODE,
268 CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE
269 )
270 }
271 FocusMode.AUTO -> {
272 params.captureRequestBuilder?.set(
273 CaptureRequest.CONTROL_AF_TRIGGER,
274 CameraMetadata.CONTROL_AF_TRIGGER_IDLE
275 )
276 }
277 FocusMode.FIXED -> {}
278 }
279
280 // Disable HDR+ for Pixel devices
281 // This is a hack, Pixel devices do not have Sepia mode, but this forces HDR+ off
282 if (android.os.Build.MANUFACTURER.equals("Google")) {
283 // params.captureRequestBuilder?.set(CaptureRequest.CONTROL_EFFECT_MODE,
284 // CaptureRequest.CONTROL_EFFECT_MODE_SEPIA)
285 }
286
287 // Orientation
288 @Suppress("DEPRECATION") /* defaultDisplay */
289 val rotation = activity.windowManager.defaultDisplay.rotation
290 val capturedImageRotation = getOrientation(params, rotation)
291 params.captureRequestBuilder?.set(
292 CaptureRequest.JPEG_ORIENTATION,
293 capturedImageRotation
294 )
295
296 // Flash
297 setAutoFlash(params, params.captureRequestBuilder)
298
299 val captureCallback = Camera2CaptureCallback(activity, params, testConfig)
300 params.camera2CaptureSession?.capture(
301 params.captureRequestBuilder?.build()!!,
302 captureCallback,
303 params.backgroundHandler
304 )
305 }
306 } catch (e: CameraAccessException) {
307 e.printStackTrace()
308 } catch (e: IllegalStateException) {
309 logd("captureStillPicture IllegalStateException, aborting: " + e.message)
310 }
311 }
312
313 /** Close preview stream and camera device. If this was a switch test, restore the camera id */
camera2CloseCameranull314 fun camera2CloseCamera(params: CameraParams?, testConfig: TestConfig) {
315 if (params == null) return
316
317 MainActivity.logd("closePreviewAndCamera: " + params.id)
318 if (params.isPreviewing) {
319 params.timer.previewCloseStart = System.currentTimeMillis()
320 params.camera2CaptureSession?.close()
321 } else {
322 params.timer.cameraCloseStart = System.currentTimeMillis()
323 params.device?.close()
324 }
325
326 if (
327 (testConfig.currentRunningTest == TestType.SWITCH_CAMERA) ||
328 (testConfig.currentRunningTest == TestType.MULTI_SWITCH)
329 ) {
330 params.id = testConfig.switchTestRealCameraId // Restore the actual camera ID
331 }
332 }
333
334 /** An abort request has been received. Abandon everything */
camera2Abortnull335 fun camera2Abort(activity: MainActivity, params: CameraParams) {
336 params.camera2CaptureSession?.abortCaptures()
337 activity.stopBackgroundThread(params)
338 }
339