[go: nahoru, domu]

blob: 2e0309f335ec9542a4afcdff1db6b02c9c29f3c4 [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
Xi Zhangd948fa82019-11-04 13:17:05 -080023import android.util.Size
Emilie Robertse1287052019-04-25 15:17:10 -070024import android.view.ViewGroup
weginlee5a8d8212019-10-23 19:11:33 +080025import androidx.annotation.experimental.UseExperimental
Emilie Robertse1287052019-04-25 15:17:10 -070026import androidx.camera.camera2.Camera2Config
weginlee5a8d8212019-10-23 19:11:33 +080027import androidx.camera.camera2.ExperimentalCamera2Interop
Trevor McGuirea743e1f2019-10-28 10:42:09 -070028import androidx.camera.core.CameraSelector
Emilie Robertse1287052019-04-25 15:17:10 -070029import androidx.camera.core.ImageCapture
30import androidx.camera.core.ImageCaptureConfig
leo huang0e830e92019-10-24 16:10:40 +080031import androidx.camera.core.LensFacing
Emilie Robertse1287052019-04-25 15:17:10 -070032import androidx.camera.core.Preview
Xi Zhangd948fa82019-11-04 13:17:05 -080033import androidx.camera.core.PreviewSurfaceProviders
34import androidx.camera.core.PreviewSurfaceProviders.createSurfaceTextureProvider
WenHung_Teng5cc433b2019-08-26 11:40:45 +080035import androidx.camera.core.impl.utils.executor.CameraXExecutors
36import androidx.camera.integration.antelope.CameraParams
Emilie Robertse1287052019-04-25 15:17:10 -070037import androidx.camera.integration.antelope.CameraXImageAvailableListener
38import androidx.camera.integration.antelope.CustomLifecycle
39import androidx.camera.integration.antelope.FocusMode
40import androidx.camera.integration.antelope.MainActivity
41import androidx.camera.integration.antelope.MainActivity.Companion.logd
42import androidx.camera.integration.antelope.PrefHelper
43import androidx.camera.integration.antelope.TestConfig
44import androidx.camera.integration.antelope.TestType
Franklin Wu4d89be12019-10-23 15:38:43 -070045import androidx.camera.lifecycle.LifecycleCameraProvider
WenHung_Teng5cc433b2019-08-26 11:40:45 +080046import androidx.lifecycle.LifecycleOwner
Emilie Robertse1287052019-04-25 15:17:10 -070047
48/**
49 * Opens the camera using the Camera X API and starts the open counter. The open call will complete
50 * in the DeviceStateCallback asynchronously. For switch tests, the camera id will be swizzling so
51 * the original camera id is saved.
52 *
53 * CameraX manages its lifecycle internally, for the purpose of repeated testing, Antelope uses a
54 * custom lifecycle to allow for starting new tests cleanly which is started here.
55 *
56 * All the needed Cmaera X use cases should be bound before starting the lifecycle. Depending on
57 * the test, bind either the preview case, or both the preview and image capture case.
58 */
59internal fun cameraXOpenCamera(
60 activity: MainActivity,
61 params: CameraParams,
62 testConfig: TestConfig
63) {
64
65 try {
66 // TODO make the switch test methodology more robust and handle physical cameras
67 // Currently we swap out the ids behind the scenes
68 // This requires to save the actual camera id for after the test
69 if ((testConfig.currentRunningTest == TestType.SWITCH_CAMERA) ||
husaynhakeem687d79a2019-11-13 16:33:20 -080070 (testConfig.currentRunningTest == TestType.MULTI_SWITCH)
71 ) {
Emilie Robertse1287052019-04-25 15:17:10 -070072 testConfig.switchTestRealCameraId = params.id // Save the actual camera ID
73 params.id = testConfig.switchTestCurrentCamera
74 }
75
76 params.cameraXDeviceStateCallback = CameraXDeviceStateCallback(params, activity, testConfig)
77 params.cameraXPreviewSessionStateCallback =
78 CameraXPreviewSessionStateCallback(activity, params, testConfig)
79
80 if (params.cameraXDeviceStateCallback != null &&
husaynhakeem687d79a2019-11-13 16:33:20 -080081 params.cameraXPreviewSessionStateCallback != null
82 ) {
83 params.cameraXPreviewBuilder =
Trevor McGuirea743e1f2019-10-28 10:42:09 -070084 cameraXPreviewUseCaseBuilder(
85 testConfig.focusMode,
Emilie Robertse1287052019-04-25 15:17:10 -070086 params.cameraXDeviceStateCallback!!,
husaynhakeem687d79a2019-11-13 16:33:20 -080087 params.cameraXPreviewSessionStateCallback!!
88 )
Emilie Robertse1287052019-04-25 15:17:10 -070089 }
90
91 if (!params.cameraXLifecycle.isFinished()) {
92 logd("Lifecycle not finished, finishing it.")
93 params.cameraXLifecycle.pauseAndStop()
94 params.cameraXLifecycle.finish()
95 }
96 params.cameraXLifecycle = CustomLifecycle()
97
98 val lifecycleOwner: LifecycleOwner = params.cameraXLifecycle
husaynhakeem687d79a2019-11-13 16:33:20 -080099 val previewUseCase = params.cameraXPreviewBuilder.build()
Emilie Robertse1287052019-04-25 15:17:10 -0700100
101 // Set preview to observe the surface texture
102 activity.runOnUiThread {
Xi Zhangd948fa82019-11-04 13:17:05 -0800103 previewUseCase.previewSurfaceCallback = createSurfaceTextureProvider(
104 object : PreviewSurfaceProviders.SurfaceTextureCallback {
Xi Zhanga3762c52019-10-22 08:09:32 -0700105
Xi Zhangd948fa82019-11-04 13:17:05 -0800106 override fun onSafeToRelease(surfaceTexture: SurfaceTexture) {
107 surfaceTexture.release()
Emilie Robertse1287052019-04-25 15:17:10 -0700108 }
Xi Zhangd948fa82019-11-04 13:17:05 -0800109
110 override fun onSurfaceTextureReady(
111 surfaceTexture: SurfaceTexture,
112 resolution: Size
113 ) {
114 if (!isCameraSurfaceTextureReleased(surfaceTexture)) {
115 val viewGroup = params.cameraXPreviewTexture?.parent as ViewGroup
116 viewGroup.removeView(params.cameraXPreviewTexture)
117 viewGroup.addView(params.cameraXPreviewTexture)
118 params.cameraXPreviewTexture?.surfaceTexture = surfaceTexture
119 }
120 }
121 })
Emilie Robertse1287052019-04-25 15:17:10 -0700122 }
123
Trevor McGuirea743e1f2019-10-28 10:42:09 -0700124 // TODO: As of 0.3.0 CameraX can only use front and back cameras.
125 // Update in future versions
126 val cameraXcameraID = if (params.id.equals("0")) LensFacing.BACK else LensFacing.FRONT
127 val cameraSelector = CameraSelector.Builder().requireLensFacing(cameraXcameraID).build()
Emilie Robertse1287052019-04-25 15:17:10 -0700128 when (testConfig.currentRunningTest) {
129 // Only the preview is required
130 TestType.PREVIEW,
131 TestType.SWITCH_CAMERA,
132 TestType.MULTI_SWITCH -> {
133 params.timer.openStart = System.currentTimeMillis()
134 activity.runOnUiThread {
Trevor McGuirea743e1f2019-10-28 10:42:09 -0700135 LifecycleCameraProvider.bindToLifecycle(
136 lifecycleOwner, cameraSelector,
137 previewUseCase
138 )
Emilie Robertse1287052019-04-25 15:17:10 -0700139 params.cameraXLifecycle.start()
140 }
141 }
142 else -> {
143 // Both preview and image capture are needed
144 params.cameraXCaptureSessionCallback =
145 CameraXCaptureSessionCallback(activity, params, testConfig)
146
147 if (params.cameraXDeviceStateCallback != null &&
husaynhakeem687d79a2019-11-13 16:33:20 -0800148 params.cameraXCaptureSessionCallback != null
149 ) {
150 params.cameraXCaptureBuilder =
Trevor McGuirea743e1f2019-10-28 10:42:09 -0700151 cameraXImageCaptureUseCaseBuilder(
152 testConfig.focusMode,
Emilie Robertse1287052019-04-25 15:17:10 -0700153 params.cameraXDeviceStateCallback!!,
husaynhakeem687d79a2019-11-13 16:33:20 -0800154 params.cameraXCaptureSessionCallback!!
155 )
Emilie Robertse1287052019-04-25 15:17:10 -0700156 }
157
husaynhakeem687d79a2019-11-13 16:33:20 -0800158 params.cameraXImageCaptureUseCase = params.cameraXCaptureBuilder.build()
Emilie Robertse1287052019-04-25 15:17:10 -0700159
160 params.timer.openStart = System.currentTimeMillis()
Trevor McGuirea743e1f2019-10-28 10:42:09 -0700161
Emilie Robertse1287052019-04-25 15:17:10 -0700162 activity.runOnUiThread {
Trevor McGuirea743e1f2019-10-28 10:42:09 -0700163 LifecycleCameraProvider.bindToLifecycle(
164 lifecycleOwner, cameraSelector,
165 previewUseCase, params.cameraXImageCaptureUseCase
166 )
Emilie Robertse1287052019-04-25 15:17:10 -0700167 params.cameraXLifecycle.start()
168 }
169 }
170 }
171 } catch (e: Exception) {
172 MainActivity.logd("cameraXOpenCamera exception: " + params.id)
173 e.printStackTrace()
174 }
175}
176
177/**
178 * End Camera X custom lifecycle, unbind use cases, and start timing the camera close.
179 */
180internal fun closeCameraX(activity: MainActivity, params: CameraParams, testConfig: TestConfig) {
181 logd("In closecameraX, camera: " + params.id + ", test: " + testConfig.currentRunningTest)
182
183 params.timer.cameraCloseStart = System.currentTimeMillis()
184
185 if (!params.cameraXLifecycle.isFinished()) {
186 params.cameraXLifecycle.pauseAndStop()
187 params.cameraXLifecycle.finish()
188
189 // CameraX calls need to be on the main thread
Scott Nien240ad8f2019-10-15 11:50:35 +0800190 activity.runOnUiThread {
Franklin Wu4d89be12019-10-23 15:38:43 -0700191 LifecycleCameraProvider.unbindAll()
Emilie Robertse1287052019-04-25 15:17:10 -0700192 }
193 }
194 if ((testConfig.currentRunningTest == TestType.SWITCH_CAMERA) ||
husaynhakeem687d79a2019-11-13 16:33:20 -0800195 (testConfig.currentRunningTest == TestType.MULTI_SWITCH)
196 ) {
Emilie Robertse1287052019-04-25 15:17:10 -0700197 params.id = testConfig.switchTestRealCameraId // Restore the actual camera ID
198 }
199
200 params.isOpen = false
201}
202
203/**
204 * Proceed to take and measure a still image capture.
205 */
206internal fun cameraXTakePicture(
207 activity: MainActivity,
208 params: CameraParams,
209 testConfig: TestConfig
210) {
211 if (params.cameraXLifecycle.isFinished()) {
212 cameraXAbort(activity, params, testConfig)
213 return
214 }
215
216 logd("CameraX TakePicture: capture start.")
217
218 // Pause in multi-captures to make sure HDR routines don't get overloaded
husaynhakeem687d79a2019-11-13 16:33:20 -0800219 logd(
220 "CameraX TakePicture. Pausing for " +
221 PrefHelper.getPreviewBuffer(activity) + "ms to let preview run."
222 )
Xi Zhanga3762c52019-10-22 08:09:32 -0700223
Emilie Robertse1287052019-04-25 15:17:10 -0700224 params.timer.previewFillStart = System.currentTimeMillis()
225 Thread.sleep(PrefHelper.getPreviewBuffer(activity))
226 params.timer.previewFillEnd = System.currentTimeMillis()
227
228 params.timer.captureStart = System.currentTimeMillis()
229 params.timer.autofocusStart = System.currentTimeMillis()
230 params.timer.autofocusEnd = System.currentTimeMillis()
231
232 logd("Capture timer started: " + params.timer.captureStart)
233 activity.runOnUiThread {
234 params.cameraXImageCaptureUseCase
WenHung_Teng5cc433b2019-08-26 11:40:45 +0800235 .takePicture(
236 CameraXExecutors.mainThreadExecutor(),
237 CameraXImageAvailableListener(activity, params, testConfig)
238 )
Emilie Robertse1287052019-04-25 15:17:10 -0700239 }
240}
241
242/**
243 * An abort request has been received. Abandon everything
244 */
245internal fun cameraXAbort(activity: MainActivity, params: CameraParams, testConfig: TestConfig) {
246 closeCameraX(activity, params, testConfig)
247 return
248}
249
250/**
251 * Try to determine if a SurfaceTexture is released.
252 *
253 * Prior to SDK 26 there was not built in mechanism for this. This method relies on expected
254 * exceptions being thrown if a released SurfaceTexture is updated.
255 */
256private fun isCameraSurfaceTextureReleased(texture: SurfaceTexture): Boolean {
257 var released = false
258
259 if (26 <= android.os.Build.VERSION.SDK_INT) {
260 released = texture.isReleased
261 } else {
262 // WARNING: This relies on some implementation details of the SurfaceTexture native code.
263 // If the SurfaceTexture is released, we should get a RuntimeException. If not, we should
264 // get an IllegalStateException since we are not in the same EGL context as the camera.
265 var exception: Exception? = null
266 try {
267 texture.updateTexImage()
268 } catch (e: IllegalStateException) {
269 logd("in isCameraSurfaceTextureReleased: IllegalStateException: " + e.message)
270 exception = e
271 released = false
272 } catch (e: RuntimeException) {
273 logd("in isCameraSurfaceTextureReleased: RuntimeException: " + e.message)
274 exception = e
275 released = true
276 }
277
278 if (!released && exception == null) {
279 throw RuntimeException("Unable to determine if SurfaceTexture is released")
280 }
281 }
282
283 logd("The camera texture is: " + if (released) "RELEASED" else "NOT RELEASED")
284
285 return released
286}
287
288/**
289 * Setup the Camera X preview use case
290 */
weginlee5a8d8212019-10-23 19:11:33 +0800291@UseExperimental(markerClass = ExperimentalCamera2Interop::class)
Emilie Robertse1287052019-04-25 15:17:10 -0700292private fun cameraXPreviewUseCaseBuilder(
Emilie Robertse1287052019-04-25 15:17:10 -0700293 focusMode: FocusMode,
294 deviceStateCallback: CameraDevice.StateCallback,
295 sessionCaptureStateCallback: CameraCaptureSession.StateCallback
husaynhakeem687d79a2019-11-13 16:33:20 -0800296): Preview.Builder {
Emilie Robertse1287052019-04-25 15:17:10 -0700297
husaynhakeem687d79a2019-11-13 16:33:20 -0800298 val configBuilder = Preview.Builder()
Emilie Robertse1287052019-04-25 15:17:10 -0700299 Camera2Config.Extender(configBuilder)
300 .setDeviceStateCallback(deviceStateCallback)
301 .setSessionStateCallback(sessionCaptureStateCallback)
Scott Nien240ad8f2019-10-15 11:50:35 +0800302 // TODO(b/142915154): Enables focusMode when CameraX support direct AF mode setting.
303
304 // Prints a log to suppress "fix Parameter 'focusMode' is never used" build error"
305 Log.d("Antelope", "focusMode($focusMode) Not enabled.")
husaynhakeem687d79a2019-11-13 16:33:20 -0800306 return configBuilder
Emilie Robertse1287052019-04-25 15:17:10 -0700307}
308
309/**
310 * Setup the Camera X image capture use case
311 */
weginlee5a8d8212019-10-23 19:11:33 +0800312@UseExperimental(markerClass = ExperimentalCamera2Interop::class)
Emilie Robertse1287052019-04-25 15:17:10 -0700313private fun cameraXImageCaptureUseCaseBuilder(
Emilie Robertse1287052019-04-25 15:17:10 -0700314 focusMode: FocusMode,
315 deviceStateCallback:
316 CameraDevice.StateCallback,
317 sessionCaptureCallback: CameraCaptureSession.CaptureCallback
husaynhakeem687d79a2019-11-13 16:33:20 -0800318): ImageCaptureConfig.Builder {
Emilie Robertse1287052019-04-25 15:17:10 -0700319
Emilie Robertse1287052019-04-25 15:17:10 -0700320 val configBuilder = ImageCaptureConfig.Builder()
Emilie Robertse1287052019-04-25 15:17:10 -0700321 .setCaptureMode(ImageCapture.CaptureMode.MAX_QUALITY)
322 Camera2Config.Extender(configBuilder)
323 .setDeviceStateCallback(deviceStateCallback)
324 .setSessionCaptureCallback(sessionCaptureCallback)
Scott Nien240ad8f2019-10-15 11:50:35 +0800325 // TODO(b/142915154): Enables focusMode when CameraX support direct AF mode setting.
Emilie Robertse1287052019-04-25 15:17:10 -0700326
Scott Nien240ad8f2019-10-15 11:50:35 +0800327 // Prints a log to suppress "fix Parameter 'focusMode' is never used" build error"
328 Log.d("Antelope", "focusMode($focusMode) Not enabled.")
husaynhakeem687d79a2019-11-13 16:33:20 -0800329 return configBuilder
Emilie Robertse1287052019-04-25 15:17:10 -0700330}