[go: nahoru, domu]

blob: cdc8f15256fa8cad4134ef55854d715ead7ad2f3 [file] [log] [blame]
Emilie Robertse1287052019-04-25 15:17:10 -07001/*
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
17package androidx.camera.integration.antelope.cameracontrollers
18
Emilie Robertse1287052019-04-25 15:17:10 -070019import android.graphics.SurfaceTexture
20import android.hardware.camera2.CameraCaptureSession
21import android.hardware.camera2.CameraDevice
22import android.hardware.camera2.CaptureRequest
23import android.view.ViewGroup
Emilie Robertse1287052019-04-25 15:17:10 -070024import androidx.camera.camera2.Camera2Config
25import androidx.camera.core.CameraX
26import androidx.camera.core.ImageCapture
27import androidx.camera.core.ImageCaptureConfig
28import androidx.camera.core.Preview
29import androidx.camera.core.PreviewConfig
WenHung_Teng5cc433b2019-08-26 11:40:45 +080030import androidx.camera.core.impl.utils.executor.CameraXExecutors
31import androidx.camera.integration.antelope.CameraParams
Emilie Robertse1287052019-04-25 15:17:10 -070032import androidx.camera.integration.antelope.CameraXImageAvailableListener
33import androidx.camera.integration.antelope.CustomLifecycle
34import androidx.camera.integration.antelope.FocusMode
35import androidx.camera.integration.antelope.MainActivity
36import androidx.camera.integration.antelope.MainActivity.Companion.logd
37import androidx.camera.integration.antelope.PrefHelper
38import androidx.camera.integration.antelope.TestConfig
39import androidx.camera.integration.antelope.TestType
WenHung_Teng5cc433b2019-08-26 11:40:45 +080040import androidx.lifecycle.LifecycleOwner
Emilie Robertse1287052019-04-25 15:17:10 -070041
42/**
43 * Opens the camera using the Camera X API and starts the open counter. The open call will complete
44 * in the DeviceStateCallback asynchronously. For switch tests, the camera id will be swizzling so
45 * the original camera id is saved.
46 *
47 * CameraX manages its lifecycle internally, for the purpose of repeated testing, Antelope uses a
48 * custom lifecycle to allow for starting new tests cleanly which is started here.
49 *
50 * All the needed Cmaera X use cases should be bound before starting the lifecycle. Depending on
51 * the test, bind either the preview case, or both the preview and image capture case.
52 */
53internal fun cameraXOpenCamera(
54 activity: MainActivity,
55 params: CameraParams,
56 testConfig: TestConfig
57) {
58
59 try {
60 // TODO make the switch test methodology more robust and handle physical cameras
61 // Currently we swap out the ids behind the scenes
62 // This requires to save the actual camera id for after the test
63 if ((testConfig.currentRunningTest == TestType.SWITCH_CAMERA) ||
64 (testConfig.currentRunningTest == TestType.MULTI_SWITCH)) {
65 testConfig.switchTestRealCameraId = params.id // Save the actual camera ID
66 params.id = testConfig.switchTestCurrentCamera
67 }
68
69 params.cameraXDeviceStateCallback = CameraXDeviceStateCallback(params, activity, testConfig)
70 params.cameraXPreviewSessionStateCallback =
71 CameraXPreviewSessionStateCallback(activity, params, testConfig)
72
73 if (params.cameraXDeviceStateCallback != null &&
74 params.cameraXPreviewSessionStateCallback != null) {
75 params.cameraXPreviewConfig =
76 cameraXPreviewUseCaseBuilder(params.id, testConfig.focusMode,
77 params.cameraXDeviceStateCallback!!,
78 params.cameraXPreviewSessionStateCallback!!)
79 }
80
81 if (!params.cameraXLifecycle.isFinished()) {
82 logd("Lifecycle not finished, finishing it.")
83 params.cameraXLifecycle.pauseAndStop()
84 params.cameraXLifecycle.finish()
85 }
86 params.cameraXLifecycle = CustomLifecycle()
87
88 val lifecycleOwner: LifecycleOwner = params.cameraXLifecycle
89 val previewUseCase = Preview(params.cameraXPreviewConfig)
90
91 // Set preview to observe the surface texture
92 activity.runOnUiThread {
93 previewUseCase.setOnPreviewOutputUpdateListener {
94 viewFinderOutput: Preview.PreviewOutput? ->
95 if (viewFinderOutput?.surfaceTexture != null) {
96 if (!isCameraSurfaceTextureReleased(viewFinderOutput.surfaceTexture)) {
97 // View swizzling required to for the view hierarchy to update correctly
98 val viewGroup = params.cameraXPreviewTexture?.parent as ViewGroup
99 viewGroup.removeView(params.cameraXPreviewTexture)
100 viewGroup.addView(params.cameraXPreviewTexture)
101 params.cameraXPreviewTexture?.surfaceTexture =
102 viewFinderOutput.surfaceTexture
103 }
104 }
105 }
106 }
107
108 when (testConfig.currentRunningTest) {
109 // Only the preview is required
110 TestType.PREVIEW,
111 TestType.SWITCH_CAMERA,
112 TestType.MULTI_SWITCH -> {
113 params.timer.openStart = System.currentTimeMillis()
114 activity.runOnUiThread {
115 CameraX.bindToLifecycle(lifecycleOwner, previewUseCase)
116 params.cameraXLifecycle.start()
117 }
118 }
119 else -> {
120 // Both preview and image capture are needed
121 params.cameraXCaptureSessionCallback =
122 CameraXCaptureSessionCallback(activity, params, testConfig)
123
124 if (params.cameraXDeviceStateCallback != null &&
125 params.cameraXCaptureSessionCallback != null) {
126 params.cameraXCaptureConfig =
127 cameraXImageCaptureUseCaseBuilder(params.id, testConfig.focusMode,
128 params.cameraXDeviceStateCallback!!,
129 params.cameraXCaptureSessionCallback!!)
130 }
131
132 params.cameraXImageCaptureUseCase = ImageCapture(params.cameraXCaptureConfig)
133
134 params.timer.openStart = System.currentTimeMillis()
135 activity.runOnUiThread {
136 CameraX.bindToLifecycle(lifecycleOwner, previewUseCase,
137 params.cameraXImageCaptureUseCase)
138 params.cameraXLifecycle.start()
139 }
140 }
141 }
142 } catch (e: Exception) {
143 MainActivity.logd("cameraXOpenCamera exception: " + params.id)
144 e.printStackTrace()
145 }
146}
147
148/**
149 * End Camera X custom lifecycle, unbind use cases, and start timing the camera close.
150 */
151internal fun closeCameraX(activity: MainActivity, params: CameraParams, testConfig: TestConfig) {
152 logd("In closecameraX, camera: " + params.id + ", test: " + testConfig.currentRunningTest)
153
154 params.timer.cameraCloseStart = System.currentTimeMillis()
155
156 if (!params.cameraXLifecycle.isFinished()) {
157 params.cameraXLifecycle.pauseAndStop()
158 params.cameraXLifecycle.finish()
159
160 // CameraX calls need to be on the main thread
161 activity.run {
162 CameraX.unbindAll()
163 }
164 }
165 if ((testConfig.currentRunningTest == TestType.SWITCH_CAMERA) ||
166 (testConfig.currentRunningTest == TestType.MULTI_SWITCH)) {
167 params.id = testConfig.switchTestRealCameraId // Restore the actual camera ID
168 }
169
170 params.isOpen = false
171}
172
173/**
174 * Proceed to take and measure a still image capture.
175 */
176internal fun cameraXTakePicture(
177 activity: MainActivity,
178 params: CameraParams,
179 testConfig: TestConfig
180) {
181 if (params.cameraXLifecycle.isFinished()) {
182 cameraXAbort(activity, params, testConfig)
183 return
184 }
185
186 logd("CameraX TakePicture: capture start.")
187
188 // Pause in multi-captures to make sure HDR routines don't get overloaded
189 logd("CameraX TakePicture. Pausing for " +
190 PrefHelper.getPreviewBuffer(activity) + "ms to let preview run.")
191 params.timer.previewFillStart = System.currentTimeMillis()
192 Thread.sleep(PrefHelper.getPreviewBuffer(activity))
193 params.timer.previewFillEnd = System.currentTimeMillis()
194
195 params.timer.captureStart = System.currentTimeMillis()
196 params.timer.autofocusStart = System.currentTimeMillis()
197 params.timer.autofocusEnd = System.currentTimeMillis()
198
199 logd("Capture timer started: " + params.timer.captureStart)
200 activity.runOnUiThread {
201 params.cameraXImageCaptureUseCase
WenHung_Teng5cc433b2019-08-26 11:40:45 +0800202 .takePicture(
203 CameraXExecutors.mainThreadExecutor(),
204 CameraXImageAvailableListener(activity, params, testConfig)
205 )
Emilie Robertse1287052019-04-25 15:17:10 -0700206 }
207}
208
209/**
210 * An abort request has been received. Abandon everything
211 */
212internal fun cameraXAbort(activity: MainActivity, params: CameraParams, testConfig: TestConfig) {
213 closeCameraX(activity, params, testConfig)
214 return
215}
216
217/**
218 * Try to determine if a SurfaceTexture is released.
219 *
220 * Prior to SDK 26 there was not built in mechanism for this. This method relies on expected
221 * exceptions being thrown if a released SurfaceTexture is updated.
222 */
223private fun isCameraSurfaceTextureReleased(texture: SurfaceTexture): Boolean {
224 var released = false
225
226 if (26 <= android.os.Build.VERSION.SDK_INT) {
227 released = texture.isReleased
228 } else {
229 // WARNING: This relies on some implementation details of the SurfaceTexture native code.
230 // If the SurfaceTexture is released, we should get a RuntimeException. If not, we should
231 // get an IllegalStateException since we are not in the same EGL context as the camera.
232 var exception: Exception? = null
233 try {
234 texture.updateTexImage()
235 } catch (e: IllegalStateException) {
236 logd("in isCameraSurfaceTextureReleased: IllegalStateException: " + e.message)
237 exception = e
238 released = false
239 } catch (e: RuntimeException) {
240 logd("in isCameraSurfaceTextureReleased: RuntimeException: " + e.message)
241 exception = e
242 released = true
243 }
244
245 if (!released && exception == null) {
246 throw RuntimeException("Unable to determine if SurfaceTexture is released")
247 }
248 }
249
250 logd("The camera texture is: " + if (released) "RELEASED" else "NOT RELEASED")
251
252 return released
253}
254
255/**
256 * Setup the Camera X preview use case
257 */
258private fun cameraXPreviewUseCaseBuilder(
259 id: String,
260 focusMode: FocusMode,
261 deviceStateCallback: CameraDevice.StateCallback,
262 sessionCaptureStateCallback: CameraCaptureSession.StateCallback
263): PreviewConfig {
264
265 // TODO: As of 0.3.0 CameraX can only use front and back cameras. Update in future versions
266 val cameraXcameraID = if (id.equals("0")) CameraX.LensFacing.BACK else CameraX.LensFacing.FRONT
267 val configBuilder = PreviewConfig.Builder()
268 .setLensFacing(cameraXcameraID)
269 Camera2Config.Extender(configBuilder)
270 .setDeviceStateCallback(deviceStateCallback)
271 .setSessionStateCallback(sessionCaptureStateCallback)
272 .setCaptureRequestOption(CaptureRequest.CONTROL_AF_MODE,
273 when (focusMode) {
274 FocusMode.AUTO -> CaptureRequest.CONTROL_AF_MODE_AUTO
275 FocusMode.CONTINUOUS -> CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE
276 FocusMode.FIXED -> CaptureRequest.CONTROL_AF_MODE_AUTO
277 })
278 return configBuilder.build()
279}
280
281/**
282 * Setup the Camera X image capture use case
283 */
284private fun cameraXImageCaptureUseCaseBuilder(
285 id: String,
286 focusMode: FocusMode,
287 deviceStateCallback:
288 CameraDevice.StateCallback,
289 sessionCaptureCallback: CameraCaptureSession.CaptureCallback
290): ImageCaptureConfig {
291
292 // TODO: As of 0.3.0 CameraX can only use front and back cameras. Update in future versions
293 val cameraXcameraID = if (id.equals("0")) CameraX.LensFacing.BACK else CameraX.LensFacing.FRONT
294
295 val configBuilder = ImageCaptureConfig.Builder()
296 .setLensFacing(cameraXcameraID)
297 .setCaptureMode(ImageCapture.CaptureMode.MAX_QUALITY)
298 Camera2Config.Extender(configBuilder)
299 .setDeviceStateCallback(deviceStateCallback)
300 .setSessionCaptureCallback(sessionCaptureCallback)
301 .setCaptureRequestOption(CaptureRequest.CONTROL_AF_MODE,
302 when (focusMode) {
303 FocusMode.AUTO -> CaptureRequest.CONTROL_AF_MODE_AUTO
304 FocusMode.CONTINUOUS -> CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE
305 FocusMode.FIXED -> CaptureRequest.CONTROL_AF_MODE_AUTO
306 })
307
308 return configBuilder.build()
309}