[go: nahoru, domu]

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