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