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