[go: nahoru, domu]

blob: 9f6147287993f8a06e963cb43eed90024f7d94d3 [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
Franklin Wu58ee9b32019-11-21 15:25:48 -080023import android.view.Surface
Emilie Robertse1287052019-04-25 15:17:10 -070024import android.view.ViewGroup
weginlee5a8d8212019-10-23 19:11:33 +080025import androidx.annotation.experimental.UseExperimental
Trevor McGuire4479d612019-11-22 18:03:31 -080026import androidx.camera.camera2.interop.Camera2Interop
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
leo huang0e830e92019-10-24 16:10:40 +080030import androidx.camera.core.LensFacing
Emilie Robertse1287052019-04-25 15:17:10 -070031import androidx.camera.core.Preview
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
Trevor McGuirecf8e22b2019-11-14 22:12:36 -080042import androidx.camera.lifecycle.ProcessCameraProvider
WenHung_Teng5cc433b2019-08-26 11:40:45 +080043import androidx.lifecycle.LifecycleOwner
Franklin Wu58ee9b32019-11-21 15:25:48 -080044import com.google.common.util.concurrent.Futures
Trevor McGuirecf8e22b2019-11-14 22:12:36 -080045import kotlinx.coroutines.Dispatchers
46import kotlinx.coroutines.GlobalScope
47import kotlinx.coroutines.guava.await
48import kotlinx.coroutines.launch
Emilie Robertse1287052019-04-25 15:17:10 -070049
50/**
51 * Opens the camera using the Camera X API and starts the open counter. The open call will complete
52 * in the DeviceStateCallback asynchronously. For switch tests, the camera id will be swizzling so
53 * the original camera id is saved.
54 *
55 * CameraX manages its lifecycle internally, for the purpose of repeated testing, Antelope uses a
56 * custom lifecycle to allow for starting new tests cleanly which is started here.
57 *
58 * All the needed Cmaera X use cases should be bound before starting the lifecycle. Depending on
59 * the test, bind either the preview case, or both the preview and image capture case.
60 */
61internal fun cameraXOpenCamera(
62 activity: MainActivity,
63 params: CameraParams,
64 testConfig: TestConfig
65) {
66
67 try {
68 // TODO make the switch test methodology more robust and handle physical cameras
69 // Currently we swap out the ids behind the scenes
70 // This requires to save the actual camera id for after the test
71 if ((testConfig.currentRunningTest == TestType.SWITCH_CAMERA) ||
husaynhakeem687d79a2019-11-13 16:33:20 -080072 (testConfig.currentRunningTest == TestType.MULTI_SWITCH)
73 ) {
Emilie Robertse1287052019-04-25 15:17:10 -070074 testConfig.switchTestRealCameraId = params.id // Save the actual camera ID
75 params.id = testConfig.switchTestCurrentCamera
76 }
77
78 params.cameraXDeviceStateCallback = CameraXDeviceStateCallback(params, activity, testConfig)
79 params.cameraXPreviewSessionStateCallback =
80 CameraXPreviewSessionStateCallback(activity, params, testConfig)
81
82 if (params.cameraXDeviceStateCallback != null &&
husaynhakeem687d79a2019-11-13 16:33:20 -080083 params.cameraXPreviewSessionStateCallback != null
84 ) {
85 params.cameraXPreviewBuilder =
Trevor McGuirea743e1f2019-10-28 10:42:09 -070086 cameraXPreviewUseCaseBuilder(
87 testConfig.focusMode,
Emilie Robertse1287052019-04-25 15:17:10 -070088 params.cameraXDeviceStateCallback!!,
husaynhakeem687d79a2019-11-13 16:33:20 -080089 params.cameraXPreviewSessionStateCallback!!
90 )
Emilie Robertse1287052019-04-25 15:17:10 -070091 }
92
93 if (!params.cameraXLifecycle.isFinished()) {
94 logd("Lifecycle not finished, finishing it.")
95 params.cameraXLifecycle.pauseAndStop()
96 params.cameraXLifecycle.finish()
97 }
98 params.cameraXLifecycle = CustomLifecycle()
99
100 val lifecycleOwner: LifecycleOwner = params.cameraXLifecycle
husaynhakeem687d79a2019-11-13 16:33:20 -0800101 val previewUseCase = params.cameraXPreviewBuilder.build()
Emilie Robertse1287052019-04-25 15:17:10 -0700102
103 // Set preview to observe the surface texture
104 activity.runOnUiThread {
Franklin Wu58ee9b32019-11-21 15:25:48 -0800105 previewUseCase.previewSurfaceProvider =
106 Preview.PreviewSurfaceProvider { resolution, surfaceReleaseFuture ->
107 // Create the SurfaceTexture and Surface
108 val surfaceTexture = SurfaceTexture(0)
109 surfaceTexture.setDefaultBufferSize(resolution.width, resolution.height)
110 surfaceTexture.detachFromGLContext()
111 val surface = Surface(surfaceTexture)
Xi Zhanga3762c52019-10-22 08:09:32 -0700112
Franklin Wu58ee9b32019-11-21 15:25:48 -0800113 // Attach the SurfaceTexture on the TextureView
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
Emilie Robertse1287052019-04-25 15:17:10 -0700119 }
Xi Zhangd948fa82019-11-04 13:17:05 -0800120
Franklin Wu58ee9b32019-11-21 15:25:48 -0800121 // Release the SurfaceTexture and Surface once camera is done with it
122 surfaceReleaseFuture.addListener(
123 Runnable {
124 surface.release()
125 surfaceTexture.release()
126 },
127 CameraXExecutors.directExecutor()
128 )
129
130 // Surface provided to camera for producing buffers into
131 Futures.immediateFuture(surface)
132 }
Emilie Robertse1287052019-04-25 15:17:10 -0700133 }
134
Trevor McGuirea743e1f2019-10-28 10:42:09 -0700135 // TODO: As of 0.3.0 CameraX can only use front and back cameras.
136 // Update in future versions
Trevor McGuirecf8e22b2019-11-14 22:12:36 -0800137 val cameraProviderFuture = ProcessCameraProvider.getInstance(activity)
Trevor McGuirea743e1f2019-10-28 10:42:09 -0700138 val cameraXcameraID = if (params.id.equals("0")) LensFacing.BACK else LensFacing.FRONT
139 val cameraSelector = CameraSelector.Builder().requireLensFacing(cameraXcameraID).build()
Emilie Robertse1287052019-04-25 15:17:10 -0700140 when (testConfig.currentRunningTest) {
141 // Only the preview is required
142 TestType.PREVIEW,
143 TestType.SWITCH_CAMERA,
144 TestType.MULTI_SWITCH -> {
145 params.timer.openStart = System.currentTimeMillis()
Trevor McGuirecf8e22b2019-11-14 22:12:36 -0800146 GlobalScope.launch(Dispatchers.Main) {
147 val cameraProvider = cameraProviderFuture.await()
148 cameraProvider.bindToLifecycle(
Trevor McGuirea743e1f2019-10-28 10:42:09 -0700149 lifecycleOwner, cameraSelector,
150 previewUseCase
151 )
Emilie Robertse1287052019-04-25 15:17:10 -0700152 params.cameraXLifecycle.start()
153 }
154 }
155 else -> {
156 // Both preview and image capture are needed
157 params.cameraXCaptureSessionCallback =
158 CameraXCaptureSessionCallback(activity, params, testConfig)
159
160 if (params.cameraXDeviceStateCallback != null &&
husaynhakeem687d79a2019-11-13 16:33:20 -0800161 params.cameraXCaptureSessionCallback != null
162 ) {
163 params.cameraXCaptureBuilder =
Trevor McGuirea743e1f2019-10-28 10:42:09 -0700164 cameraXImageCaptureUseCaseBuilder(
165 testConfig.focusMode,
Emilie Robertse1287052019-04-25 15:17:10 -0700166 params.cameraXDeviceStateCallback!!,
husaynhakeem687d79a2019-11-13 16:33:20 -0800167 params.cameraXCaptureSessionCallback!!
168 )
Emilie Robertse1287052019-04-25 15:17:10 -0700169 }
170
husaynhakeem687d79a2019-11-13 16:33:20 -0800171 params.cameraXImageCaptureUseCase = params.cameraXCaptureBuilder.build()
Emilie Robertse1287052019-04-25 15:17:10 -0700172
173 params.timer.openStart = System.currentTimeMillis()
Trevor McGuirea743e1f2019-10-28 10:42:09 -0700174
Trevor McGuirecf8e22b2019-11-14 22:12:36 -0800175 GlobalScope.launch(Dispatchers.Main) {
176 val cameraProvider = cameraProviderFuture.await()
177 cameraProvider.bindToLifecycle(
Trevor McGuirea743e1f2019-10-28 10:42:09 -0700178 lifecycleOwner, cameraSelector,
179 previewUseCase, params.cameraXImageCaptureUseCase
180 )
Emilie Robertse1287052019-04-25 15:17:10 -0700181 params.cameraXLifecycle.start()
182 }
183 }
184 }
185 } catch (e: Exception) {
186 MainActivity.logd("cameraXOpenCamera exception: " + params.id)
187 e.printStackTrace()
188 }
189}
190
191/**
192 * End Camera X custom lifecycle, unbind use cases, and start timing the camera close.
193 */
194internal fun closeCameraX(activity: MainActivity, params: CameraParams, testConfig: TestConfig) {
195 logd("In closecameraX, camera: " + params.id + ", test: " + testConfig.currentRunningTest)
196
197 params.timer.cameraCloseStart = System.currentTimeMillis()
198
199 if (!params.cameraXLifecycle.isFinished()) {
200 params.cameraXLifecycle.pauseAndStop()
201 params.cameraXLifecycle.finish()
202
203 // CameraX calls need to be on the main thread
Trevor McGuirecf8e22b2019-11-14 22:12:36 -0800204 val cameraProviderFuture = ProcessCameraProvider.getInstance(activity)
205 GlobalScope.launch(Dispatchers.Main) {
206 val cameraProvider = cameraProviderFuture.await()
207 cameraProvider.unbindAll()
Emilie Robertse1287052019-04-25 15:17:10 -0700208 }
209 }
210 if ((testConfig.currentRunningTest == TestType.SWITCH_CAMERA) ||
husaynhakeem687d79a2019-11-13 16:33:20 -0800211 (testConfig.currentRunningTest == TestType.MULTI_SWITCH)
212 ) {
Emilie Robertse1287052019-04-25 15:17:10 -0700213 params.id = testConfig.switchTestRealCameraId // Restore the actual camera ID
214 }
215
216 params.isOpen = false
217}
218
219/**
220 * Proceed to take and measure a still image capture.
221 */
222internal fun cameraXTakePicture(
223 activity: MainActivity,
224 params: CameraParams,
225 testConfig: TestConfig
226) {
227 if (params.cameraXLifecycle.isFinished()) {
228 cameraXAbort(activity, params, testConfig)
229 return
230 }
231
232 logd("CameraX TakePicture: capture start.")
233
234 // Pause in multi-captures to make sure HDR routines don't get overloaded
husaynhakeem687d79a2019-11-13 16:33:20 -0800235 logd(
236 "CameraX TakePicture. Pausing for " +
237 PrefHelper.getPreviewBuffer(activity) + "ms to let preview run."
238 )
Xi Zhanga3762c52019-10-22 08:09:32 -0700239
Emilie Robertse1287052019-04-25 15:17:10 -0700240 params.timer.previewFillStart = System.currentTimeMillis()
241 Thread.sleep(PrefHelper.getPreviewBuffer(activity))
242 params.timer.previewFillEnd = System.currentTimeMillis()
243
244 params.timer.captureStart = System.currentTimeMillis()
245 params.timer.autofocusStart = System.currentTimeMillis()
246 params.timer.autofocusEnd = System.currentTimeMillis()
247
248 logd("Capture timer started: " + params.timer.captureStart)
249 activity.runOnUiThread {
250 params.cameraXImageCaptureUseCase
WenHung_Teng5cc433b2019-08-26 11:40:45 +0800251 .takePicture(
252 CameraXExecutors.mainThreadExecutor(),
253 CameraXImageAvailableListener(activity, params, testConfig)
254 )
Emilie Robertse1287052019-04-25 15:17:10 -0700255 }
256}
257
258/**
259 * An abort request has been received. Abandon everything
260 */
261internal fun cameraXAbort(activity: MainActivity, params: CameraParams, testConfig: TestConfig) {
262 closeCameraX(activity, params, testConfig)
263 return
264}
265
266/**
267 * Try to determine if a SurfaceTexture is released.
268 *
269 * Prior to SDK 26 there was not built in mechanism for this. This method relies on expected
270 * exceptions being thrown if a released SurfaceTexture is updated.
271 */
272private fun isCameraSurfaceTextureReleased(texture: SurfaceTexture): Boolean {
273 var released = false
274
275 if (26 <= android.os.Build.VERSION.SDK_INT) {
276 released = texture.isReleased
277 } else {
278 // WARNING: This relies on some implementation details of the SurfaceTexture native code.
279 // If the SurfaceTexture is released, we should get a RuntimeException. If not, we should
280 // get an IllegalStateException since we are not in the same EGL context as the camera.
281 var exception: Exception? = null
282 try {
283 texture.updateTexImage()
284 } catch (e: IllegalStateException) {
285 logd("in isCameraSurfaceTextureReleased: IllegalStateException: " + e.message)
286 exception = e
287 released = false
288 } catch (e: RuntimeException) {
289 logd("in isCameraSurfaceTextureReleased: RuntimeException: " + e.message)
290 exception = e
291 released = true
292 }
293
294 if (!released && exception == null) {
295 throw RuntimeException("Unable to determine if SurfaceTexture is released")
296 }
297 }
298
299 logd("The camera texture is: " + if (released) "RELEASED" else "NOT RELEASED")
300
301 return released
302}
303
304/**
305 * Setup the Camera X preview use case
306 */
weginlee5a8d8212019-10-23 19:11:33 +0800307@UseExperimental(markerClass = ExperimentalCamera2Interop::class)
Emilie Robertse1287052019-04-25 15:17:10 -0700308private fun cameraXPreviewUseCaseBuilder(
Emilie Robertse1287052019-04-25 15:17:10 -0700309 focusMode: FocusMode,
310 deviceStateCallback: CameraDevice.StateCallback,
311 sessionCaptureStateCallback: CameraCaptureSession.StateCallback
husaynhakeem687d79a2019-11-13 16:33:20 -0800312): Preview.Builder {
Emilie Robertse1287052019-04-25 15:17:10 -0700313
husaynhakeem687d79a2019-11-13 16:33:20 -0800314 val configBuilder = Preview.Builder()
Trevor McGuire4479d612019-11-22 18:03:31 -0800315 Camera2Interop.Extender(configBuilder)
Emilie Robertse1287052019-04-25 15:17:10 -0700316 .setDeviceStateCallback(deviceStateCallback)
317 .setSessionStateCallback(sessionCaptureStateCallback)
Scott Nien240ad8f2019-10-15 11:50:35 +0800318 // TODO(b/142915154): Enables focusMode when CameraX support direct AF mode setting.
319
320 // Prints a log to suppress "fix Parameter 'focusMode' is never used" build error"
321 Log.d("Antelope", "focusMode($focusMode) Not enabled.")
husaynhakeem687d79a2019-11-13 16:33:20 -0800322 return configBuilder
Emilie Robertse1287052019-04-25 15:17:10 -0700323}
324
325/**
326 * Setup the Camera X image capture use case
327 */
weginlee5a8d8212019-10-23 19:11:33 +0800328@UseExperimental(markerClass = ExperimentalCamera2Interop::class)
Emilie Robertse1287052019-04-25 15:17:10 -0700329private fun cameraXImageCaptureUseCaseBuilder(
Emilie Robertse1287052019-04-25 15:17:10 -0700330 focusMode: FocusMode,
331 deviceStateCallback:
332 CameraDevice.StateCallback,
333 sessionCaptureCallback: CameraCaptureSession.CaptureCallback
husaynhakeembf6c2f92019-11-18 14:49:02 -0800334): ImageCapture.Builder {
Emilie Robertse1287052019-04-25 15:17:10 -0700335
husaynhakeembf6c2f92019-11-18 14:49:02 -0800336 val configBuilder = ImageCapture.Builder()
husaynhakeemf7146672019-11-20 15:34:14 -0800337 .setCaptureMode(ImageCapture.CaptureMode.MAXIMIZE_QUALITY)
Trevor McGuire4479d612019-11-22 18:03:31 -0800338 Camera2Interop.Extender(configBuilder)
Emilie Robertse1287052019-04-25 15:17:10 -0700339 .setDeviceStateCallback(deviceStateCallback)
340 .setSessionCaptureCallback(sessionCaptureCallback)
Scott Nien240ad8f2019-10-15 11:50:35 +0800341 // TODO(b/142915154): Enables focusMode when CameraX support direct AF mode setting.
Emilie Robertse1287052019-04-25 15:17:10 -0700342
Scott Nien240ad8f2019-10-15 11:50:35 +0800343 // Prints a log to suppress "fix Parameter 'focusMode' is never used" build error"
344 Log.d("Antelope", "focusMode($focusMode) Not enabled.")
husaynhakeem687d79a2019-11-13 16:33:20 -0800345 return configBuilder
Emilie Robertse1287052019-04-25 15:17:10 -0700346}