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 // Camera 1 API is deprecated, suppress warnings as Antelope expressly tests it
18 @file:Suppress("deprecation")
19 
20 package androidx.camera.integration.antelope.cameracontrollers
21 
22 import android.hardware.Camera
23 import android.util.Size
24 import androidx.camera.integration.antelope.CameraParams
25 import androidx.camera.integration.antelope.CompareSizesByArea
26 import androidx.camera.integration.antelope.FocusMode
27 import androidx.camera.integration.antelope.ImageCaptureSize
28 import androidx.camera.integration.antelope.MainActivity
29 import androidx.camera.integration.antelope.MainActivity.Companion.logd
30 import androidx.camera.integration.antelope.PrefHelper
31 import androidx.camera.integration.antelope.TestConfig
32 import androidx.camera.integration.antelope.TestType
33 import androidx.camera.integration.antelope.testEnded
34 import androidx.camera.integration.antelope.writeFile
35 import java.util.Collections
36 
37 internal var camera1: Camera? = null
38 
39 /**
40  * Opens the camera using the Camera1 API and measures the open time synchronously. For init tests,
41  * this is the only measurement needed so end the test. Otherwise, move on to open the camera
42  * preview.
43  */
camera1OpenCameranull44 fun camera1OpenCamera(activity: MainActivity, params: CameraParams, testConfig: TestConfig) {
45     try {
46         logd("openCamera: " + params.id)
47         params.isOpen = true
48         params.timer.openStart = System.currentTimeMillis()
49 
50         logd("Camera1Switch Open camera: " + testConfig.switchTestCurrentCamera.toInt())
51 
52         if (
53             (testConfig.currentRunningTest == TestType.SWITCH_CAMERA) ||
54                 (testConfig.currentRunningTest == TestType.MULTI_SWITCH)
55         )
56             camera1 = Camera.open(testConfig.switchTestCurrentCamera.toInt())
57         else camera1 = Camera.open(testConfig.camera.toInt())
58 
59         params.timer.openEnd = System.currentTimeMillis()
60 
61         // Due to the synchronous nature of Camera1, set up Camera1 specific parameters here
62         val camera1Params: Camera.Parameters? = camera1?.parameters
63         params.cam1AFSupported =
64             camera1Params?.supportedFocusModes?.contains(Camera.Parameters.FOCUS_MODE_AUTO) ?: false
65 
66         when (testConfig.currentRunningTest) {
67             TestType.INIT -> {
68                 // Camera opened, we're done
69                 testEnded(activity, params, testConfig)
70             }
71             else -> {
72                 startCamera1Preview(activity, params, testConfig)
73             }
74         }
75     } catch (e: Exception) {
76         logd("camera1OpenCamera exception: " + params.id + ". Error: " + e.printStackTrace())
77         camera1 = null
78     }
79 }
80 
81 /**
82  * Begin the preview using the Camera 1 API and synchronously measure the time to begin the stream.
83  */
startCamera1Previewnull84 fun startCamera1Preview(activity: MainActivity, params: CameraParams, testConfig: TestConfig) {
85     val camera1Params: Camera.Parameters? = camera1?.parameters
86 
87     params.cam1AFSupported =
88         camera1Params?.supportedFocusModes?.contains(Camera.Parameters.FOCUS_MODE_AUTO) ?: false
89 
90     // Get Camera1 image capture sizes
91     // Cannot be done this before the camera is device opened
92     val cam1Sizes = camera1Params?.getSupportedPictureSizes()
93     if (null != cam1Sizes) {
94         val saneSizes: ArrayList<Size> = ArrayList()
95 
96         for (size in cam1Sizes) {
97             saneSizes.add(Size(size.width, size.height))
98         }
99 
100         params.cam1MaxSize = Collections.max(saneSizes, CompareSizesByArea())
101         params.cam1MinSize = Collections.min(saneSizes, CompareSizesByArea())
102     }
103 
104     if (ImageCaptureSize.MIN == testConfig.imageCaptureSize)
105         camera1Params?.setPictureSize(params.cam1MinSize.width, params.cam1MinSize.height)
106     else camera1Params?.setPictureSize(params.cam1MaxSize.width, params.cam1MaxSize.height)
107 
108     if (params.cam1AFSupported) {
109         if (FocusMode.CONTINUOUS == testConfig.focusMode)
110             camera1Params?.focusMode = Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE
111         else camera1Params?.focusMode = Camera.Parameters.FOCUS_MODE_AUTO
112     } else {
113         camera1Params?.focusMode = Camera.Parameters.FOCUS_MODE_FIXED
114     }
115 
116     // After changing camera parameters, set them
117     camera1?.parameters = camera1Params
118 
119     logd("startCamera1Preview: starting Camera1 preview.")
120 
121     params.isPreviewing = true
122     params.timer.previewStart = System.currentTimeMillis()
123     camera1?.startPreview()
124     camera1?.setPreviewDisplay(params.previewSurfaceView?.holder)
125     params.timer.previewEnd = System.currentTimeMillis()
126 
127     when (testConfig.currentRunningTest) {
128         TestType.PREVIEW -> {
129             testConfig.testFinished = true
130             closePreviewAndCamera(activity, params, testConfig)
131         }
132         TestType.SWITCH_CAMERA,
133         TestType.MULTI_SWITCH -> {
134             logd("Camera1Switch preview running:")
135             if (testConfig.switchTestCurrentCamera == testConfig.switchTestCameras.get(0)) {
136                 if (testConfig.testFinished) {
137                     logd("Camera1Switch preview. On 1st camera, test finished. Closing 1st camera")
138                     params.timer.switchToFirstEnd = System.currentTimeMillis()
139                     Thread.sleep(PrefHelper.getPreviewBuffer(activity)) // Let preview run
140                     closePreviewAndCamera(activity, params, testConfig)
141                 } else {
142                     logd("Camera1Switch preview. On 1st camera, Closing 1st camera, then open 2nd")
143                     Thread.sleep(PrefHelper.getPreviewBuffer(activity)) // Let preview run
144                     params.timer.switchToSecondStart = System.currentTimeMillis()
145                     closePreviewAndCamera(activity, params, testConfig)
146                 }
147             } else {
148                 logd(
149                     "Camera1Switch preview. On 2nd camera. Closing, ready to open first 1st " +
150                         "camera"
151                 )
152                 params.timer.switchToSecondEnd = System.currentTimeMillis()
153                 Thread.sleep(PrefHelper.getPreviewBuffer(activity)) // Let preview run
154                 params.timer.switchToFirstStart = System.currentTimeMillis()
155                 closePreviewAndCamera(activity, params, testConfig)
156             }
157         }
158         TestType.NONE -> {
159             closeAllCameras(activity, testConfig)
160         }
161         else -> {
162             camera1TakePicturePrep(activity, params, testConfig)
163         }
164     }
165 }
166 
167 /**
168  * Set up timers and focus mode for taking a picture with the Camera 1 API. If auto-focus is
169  * requested, begin the auto-focus timer and asynchronously begin the auto-focus routine.
170  */
camera1TakePicturePrepnull171 fun camera1TakePicturePrep(activity: MainActivity, params: CameraParams, testConfig: TestConfig) {
172     if (params.timer.isFirstPhoto) {
173         logd(
174             "camera1TakePicturePrep: 1st photo in multi-chain test. Pausing for " +
175                 PrefHelper.getPreviewBuffer(activity) +
176                 "ms to let preview run."
177         )
178         params.timer.previewFillStart = System.currentTimeMillis()
179         Thread.sleep(PrefHelper.getPreviewBuffer(activity))
180         params.timer.previewFillEnd = System.currentTimeMillis()
181         params.timer.isFirstPhoto = false
182     }
183 
184     params.timer.captureStart = System.currentTimeMillis()
185 
186     if (params.cam1AFSupported && FocusMode.AUTO == testConfig.focusMode) {
187         MainActivity.logd("camera1TakePicturePrep: starting autofocus.")
188         params.timer.autofocusStart = System.currentTimeMillis()
189         camera1?.autoFocus(Camera1AutofocusCallback(activity, params, testConfig))
190     } else {
191         camera1TakePicture(activity, params, testConfig)
192     }
193 }
194 
195 /** Initiate the capture request */
camera1TakePicturenull196 fun camera1TakePicture(activity: MainActivity, params: CameraParams, testConfig: TestConfig) {
197     val camera1JpegCallback = Camera1PictureCallback(activity, params, testConfig)
198 
199     try {
200         MainActivity.logd("camera1TakePicture: capture start. ")
201         camera1?.takePicture(null, null, camera1JpegCallback)
202     } catch (e: RuntimeException) {
203         MainActivity.logd("camera1TakePicture: runtime exception: " + e.printStackTrace())
204     }
205 }
206 
207 /** Close preview stream and camera device. If this is a switch test, begin the next step */
camera1CloseCameranull208 fun camera1CloseCamera(activity: MainActivity, params: CameraParams?, testConfig: TestConfig) {
209     if (params == null) return
210 
211     if (params.isPreviewing) {
212         params.timer.previewCloseStart = System.currentTimeMillis()
213         camera1?.stopPreview()
214         params.timer.previewCloseEnd = System.currentTimeMillis()
215         params.isPreviewing = false
216     }
217 
218     params.timer.cameraCloseStart = System.currentTimeMillis()
219     camera1?.release()
220     params.timer.cameraCloseEnd = System.currentTimeMillis()
221     params.isOpen = false
222 
223     logd("Camera 1 Close camera: camera released.")
224 
225     if (testConfig.testFinished) {
226         logd("Camera 1 Close camera: Test finished, returning")
227         testEnded(activity, params, testConfig)
228         return
229     }
230 
231     when (testConfig.currentRunningTest) {
232         TestType.SWITCH_CAMERA,
233         TestType.MULTI_SWITCH -> {
234             logd("Camera1Switch Close camera")
235             // First camera closed, now start the second
236             if (testConfig.switchTestCurrentCamera == testConfig.switchTestCameras.get(0)) {
237                 testConfig.switchTestCurrentCamera = testConfig.switchTestCameras.get(1)
238                 logd("Camera1Switch Close camera 1st camera is closed, opening the second")
239                 camera1OpenCamera(activity, params, testConfig)
240             }
241 
242             // Second camera closed, now start the first
243             else if (testConfig.switchTestCurrentCamera == testConfig.switchTestCameras.get(1)) {
244                 logd("Camera1Switch Close camera 2nd camera is closed, opening the first")
245                 testConfig.switchTestCurrentCamera = testConfig.switchTestCameras.get(0)
246                 testConfig.testFinished = true
247                 camera1OpenCamera(activity, params, testConfig)
248             }
249         }
250         else -> {
251             Unit // no-op
252         }
253     }
254 }
255 
256 /** Auto-focus is complete, record the elapsed time and request the capture */
257 class Camera1AutofocusCallback
258 internal constructor(
259     internal val activity: MainActivity,
260     internal val params: CameraParams,
261     internal val testConfig: TestConfig
262 ) : Camera.AutoFocusCallback {
263 
264     @Suppress("OVERRIDE_DEPRECATION") // b/407496822
onAutoFocusnull265     override fun onAutoFocus(p0: Boolean, p1: Camera?) {
266         MainActivity.logd("camera1AutofocusCallback: autofocus complete.")
267         params.timer.autofocusEnd = System.currentTimeMillis()
268         camera1TakePicture(activity, params, testConfig)
269     }
270 }
271 
272 /**
273  * Image capture has completed. Record the time taken, synchronously write file to disk and measure
274  * the time required. This test run is finished, call to finalize test or continue the test run.
275  */
276 class Camera1PictureCallback
277 internal constructor(
278     internal val activity: MainActivity,
279     internal val params: CameraParams,
280     internal val testConfig: TestConfig
281 ) : Camera.PictureCallback {
282 
283     @Suppress("OVERRIDE_DEPRECATION") // b/407496822
onPictureTakennull284     override fun onPictureTaken(bytes: ByteArray?, p1: Camera?) {
285 
286         params.timer.captureEnd = System.currentTimeMillis()
287 
288         // With the Camera1 API, calling takePicture() stops the preview. In order to make sure the
289         // close timings are comparable across APIs, restart it here.
290         camera1?.startPreview()
291 
292         logd("in Camera1PictureCallback onPictureTaken")
293 
294         params.timer.imageReaderStart = System.currentTimeMillis()
295         params.timer.imageReaderEnd = System.currentTimeMillis()
296         params.timer.imageSaveStart = System.currentTimeMillis()
297 
298         if (null != bytes) writeFile(activity, bytes)
299 
300         params.timer.imageSaveEnd = System.currentTimeMillis()
301 
302         testConfig.testFinished = true
303         closePreviewAndCamera(activity, params, testConfig)
304     }
305 }
306