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 package androidx.camera.integration.antelope
18 
19 import android.graphics.ImageFormat
20 import android.graphics.Rect
21 import android.hardware.camera2.CameraAccessException
22 import android.hardware.camera2.CameraCharacteristics
23 import android.hardware.camera2.CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE
24 import android.hardware.camera2.CameraManager
25 import android.hardware.camera2.CameraMetadata
26 import android.hardware.camera2.CaptureRequest
27 import android.media.ImageReader
28 import android.os.Build
29 import android.util.SparseIntArray
30 import android.view.Surface
31 import androidx.appcompat.app.AppCompatActivity
32 import androidx.camera.core.ImageCapture
33 import androidx.camera.core.Preview
34 import androidx.camera.integration.antelope.MainActivity.Companion.FIXED_FOCUS_DISTANCE
35 import androidx.camera.integration.antelope.MainActivity.Companion.cameraParams
36 import androidx.camera.integration.antelope.MainActivity.Companion.logd
37 import androidx.camera.integration.antelope.cameracontrollers.Camera2CaptureSessionCallback
38 import androidx.camera.integration.antelope.cameracontrollers.Camera2DeviceStateCallback
39 import java.util.Arrays
40 import java.util.Collections
41 
42 /** The camera API to use */
43 enum class CameraAPI(private val api: String) {
44     /** Camera 1 API */
45     CAMERA1("Camera1"),
46     /** Camera 2 API */
47     CAMERA2("Camera2"),
48     /** Camera X API */
49     CAMERAX("CameraX")
50 }
51 
52 /** The output capture size to request for captures */
53 enum class ImageCaptureSize(private val size: String) {
54     /** Request captures to be the maximum supported size for this camera sensor */
55     MAX("Max"),
56     /** Request captures to be the minimum supported size for this camera sensor */
57     MIN("Min"),
58 }
59 
60 /** The focus mechanism to request for captures */
61 enum class FocusMode(private val mode: String) {
62     /** Auto-focus */
63     AUTO("Auto"),
64     /** Continuous auto-focus */
65     CONTINUOUS("Continuous"),
66     /** For fixed-focus lenses */
67     FIXED("Fixed")
68 }
69 
70 /**
71  * For all the cameras associated with the device, populate the CameraParams values and add them to
72  * the companion object for the activity.
73  */
initializeCamerasnull74 fun initializeCameras(activity: MainActivity) {
75     val manager = activity.getSystemService(AppCompatActivity.CAMERA_SERVICE) as CameraManager
76     try {
77         val numCameras = manager.cameraIdList.size
78         for (cameraId in manager.cameraIdList) {
79             var cameraParamsValid = true
80 
81             val tempCameraParams =
82                 CameraParams().apply {
83                     val cameraChars = manager.getCameraCharacteristics(cameraId)
84                     val cameraCapabilities =
85                         cameraChars.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES)
86                             ?: IntArray(0)
87 
88                     // Check supported format.
89                     val map = cameraChars.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP)
90                     if (map == null || map.isOutputSupportedFor(ImageFormat.JPEG) == false) {
91                         cameraParamsValid = false
92                         logd(
93                             "Null streamConfigurationMap or not supporting JPEG output format " +
94                                 "in cameraId:" +
95                                 cameraId
96                         )
97                         return@apply
98                     }
99 
100                     // Multi-camera
101                     for (capability in cameraCapabilities) {
102                         if (
103                             capability ==
104                                 CameraCharacteristics
105                                     .REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA
106                         ) {
107                             hasMulti = true
108                         } else if (
109                             capability ==
110                                 CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR
111                         ) {
112                             hasManualControl = true
113                         }
114                     }
115 
116                     logd("Camera " + cameraId + " of " + numCameras)
117 
118                     id = cameraId
119                     isOpen = false
120                     hasFlash = cameraChars.get(CameraCharacteristics.FLASH_INFO_AVAILABLE) ?: false
121                     isFront =
122                         CameraCharacteristics.LENS_FACING_FRONT ==
123                             cameraChars.get(CameraCharacteristics.LENS_FACING)
124 
125                     isExternal =
126                         (Build.VERSION.SDK_INT >= 23 &&
127                             CameraCharacteristics.LENS_FACING_EXTERNAL ==
128                                 cameraChars.get(CameraCharacteristics.LENS_FACING))
129 
130                     characteristics = cameraChars
131                     focalLengths =
132                         cameraChars.get(CameraCharacteristics.LENS_INFO_AVAILABLE_FOCAL_LENGTHS)
133                             ?: FloatArray(0)
134                     smallestFocalLength = smallestFocalLength(focalLengths)
135                     minDeltaFromNormal = focalLengthMinDeltaFromNormal(focalLengths)
136 
137                     apertures =
138                         cameraChars.get(CameraCharacteristics.LENS_INFO_AVAILABLE_APERTURES)
139                             ?: FloatArray(0)
140                     largestAperture = largestAperture(apertures)
141                     minFocusDistance =
142                         cameraChars.get(CameraCharacteristics.LENS_INFO_MINIMUM_FOCUS_DISTANCE)
143                             ?: MainActivity.FIXED_FOCUS_DISTANCE
144 
145                     for (focalLength in focalLengths) {
146                         logd("In " + id + " found focalLength: " + focalLength)
147                     }
148                     logd("Smallest smallestFocalLength: " + smallestFocalLength)
149                     logd("minFocusDistance: " + minFocusDistance)
150 
151                     for (aperture in apertures) {
152                         logd("In " + id + " found aperture: " + aperture)
153                     }
154                     logd("Largest aperture: " + largestAperture)
155 
156                     if (hasManualControl) {
157                         logd("Has Manual, minFocusDistance: " + minFocusDistance)
158                     }
159 
160                     // Autofocus
161                     hasAF =
162                         minFocusDistance != FIXED_FOCUS_DISTANCE // If camera is fixed focus, no AF
163 
164                     effects =
165                         cameraChars.get(CameraCharacteristics.CONTROL_AVAILABLE_EFFECTS)
166                             ?: IntArray(0)
167                     hasSepia = effects.contains(CameraMetadata.CONTROL_EFFECT_MODE_SEPIA)
168                     hasMono = effects.contains(CameraMetadata.CONTROL_EFFECT_MODE_MONO)
169 
170                     if (hasSepia) logd("WE HAVE Sepia!")
171                     if (hasMono) logd("WE HAVE Mono!")
172 
173                     isLegacy =
174                         cameraChars.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL) ==
175                             CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY
176 
177                     val activeSensorRect: Rect = cameraChars.get(SENSOR_INFO_ACTIVE_ARRAY_SIZE)!!
178                     megapixels = (activeSensorRect.width() * activeSensorRect.height()) / 1000000
179 
180                     camera2DeviceStateCallback =
181                         Camera2DeviceStateCallback(this, activity, TestConfig())
182                     camera2CaptureSessionCallback =
183                         Camera2CaptureSessionCallback(activity, this, TestConfig())
184 
185                     previewSurfaceView = activity.binding.surfacePreview
186                     cameraXPreviewTexture = activity.binding.texturePreview
187 
188                     cameraXPreviewBuilder = Preview.Builder()
189 
190                     cameraXCaptureBuilder = ImageCapture.Builder()
191 
192                     imageAvailableListener = ImageAvailableListener(activity, this, TestConfig())
193 
194                     if (Build.VERSION.SDK_INT >= 28) {
195                         physicalCameras = cameraChars.physicalCameraIds
196                     }
197 
198                     // Get Camera2 and CameraX image capture sizes.
199                     cam2MaxSize =
200                         Collections.max(
201                             Arrays.asList(*map.getOutputSizes(ImageFormat.JPEG)),
202                             CompareSizesByArea()
203                         )
204 
205                     cam2MinSize =
206                         Collections.min(
207                             Arrays.asList(*map.getOutputSizes(ImageFormat.JPEG)),
208                             CompareSizesByArea()
209                         )
210 
211                     // Use minimum image size for preview
212                     previewSurfaceView?.holder?.setFixedSize(cam2MinSize.width, cam2MinSize.height)
213 
214                     setupImageReader(activity, this, TestConfig())
215                 }
216 
217             if (cameraParamsValid == false) {
218                 logd("Don't put Camera " + cameraId + "of " + numCameras)
219                 continue
220             } else {
221                 cameraParams.put(cameraId, tempCameraParams)
222             }
223         } // For all camera devices
224     } catch (accessError: CameraAccessException) {
225         accessError.printStackTrace()
226     }
227 }
228 
229 /**
230  * Convenience method to configure the ImageReaders required for Camera1 and Camera2 APIs.
231  *
232  * Uses JPEG image format, checks the current test configuration to determine the needed size.
233  */
setupImageReadernull234 fun setupImageReader(activity: MainActivity, params: CameraParams, testConfig: TestConfig) {
235 
236     // Only use ImageReader for Camera1 and Camera2
237     if (CameraAPI.CAMERAX != testConfig.api) {
238         params.imageAvailableListener = ImageAvailableListener(activity, params, testConfig)
239 
240         val useLargest = testConfig.imageCaptureSize == ImageCaptureSize.MAX
241 
242         val size = if (useLargest) params.cam2MaxSize else params.cam2MinSize
243 
244         params.imageReader?.close()
245         params.imageReader = ImageReader.newInstance(size.width, size.height, ImageFormat.JPEG, 5)
246         params.imageReader?.setOnImageAvailableListener(
247             params.imageAvailableListener,
248             params.backgroundHandler
249         )
250     }
251 }
252 
253 /** Finds the smallest focal length in the given array, useful for finding the widest angle lens */
smallestFocalLengthnull254 fun smallestFocalLength(focalLengths: FloatArray): Float =
255     focalLengths.minOrNull() ?: MainActivity.INVALID_FOCAL_LENGTH
256 
257 /** Finds the largest aperture in the array of focal lengths */
258 fun largestAperture(apertures: FloatArray): Float =
259     apertures.maxOrNull() ?: MainActivity.NO_APERTURE
260 
261 /** Finds the most "normal" focal length in the array of focal lengths */
262 fun focalLengthMinDeltaFromNormal(focalLengths: FloatArray): Float =
263     focalLengths.minByOrNull { Math.abs(it - MainActivity.NORMAL_FOCAL_LENGTH) } ?: Float.MAX_VALUE
264 
265 /** Adds automatic flash to the given CaptureRequest.Builder */
setAutoFlashnull266 fun setAutoFlash(params: CameraParams, requestBuilder: CaptureRequest.Builder?) {
267     try {
268         if (params.hasFlash) {
269             requestBuilder?.set(
270                 CaptureRequest.CONTROL_AE_MODE,
271                 CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH
272             )
273 
274             // Force flash always on
275             //            requestBuilder?.set(CaptureRequest.CONTROL_AE_MODE,
276             //                    CaptureRequest.CONTROL_AE_MODE_ON_ALWAYS_FLASH)
277         }
278     } catch (e: Exception) {
279         // Do nothing
280     }
281 }
282 
283 /** We have to take sensor orientation into account and rotate JPEG properly. */
getOrientationnull284 fun getOrientation(params: CameraParams, rotation: Int): Int {
285     val orientations = SparseIntArray()
286     orientations.append(Surface.ROTATION_0, 90)
287     orientations.append(Surface.ROTATION_90, 0)
288     orientations.append(Surface.ROTATION_180, 270)
289     orientations.append(Surface.ROTATION_270, 180)
290 
291     logd(
292         "Orientation: sensor: " +
293             params.characteristics?.get(CameraCharacteristics.SENSOR_ORIENTATION) +
294             " and current rotation: " +
295             orientations.get(rotation)
296     )
297     val sensorRotation: Int =
298         params.characteristics?.get(CameraCharacteristics.SENSOR_ORIENTATION) ?: 0
299     return (orientations.get(rotation) + sensorRotation + 270) % 360
300 }
301