blob: d0f5811c2cc64714ce9f4f9ab6ad3ea0a1c194a2 [file] [log] [blame]
/*
* Copyright 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package androidx.camera.integration.antelope
import android.graphics.ImageFormat
import android.graphics.Rect
import android.hardware.camera2.CameraAccessException
import android.hardware.camera2.CameraCharacteristics
import android.hardware.camera2.CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE
import android.hardware.camera2.CameraManager
import android.hardware.camera2.CameraMetadata
import android.hardware.camera2.CaptureRequest
import android.media.ImageReader
import android.os.Build
import android.util.SparseIntArray
import android.view.Surface
import androidx.appcompat.app.AppCompatActivity
import androidx.camera.core.ImageCapture
import androidx.camera.core.Preview
import androidx.camera.integration.antelope.MainActivity.Companion.FIXED_FOCUS_DISTANCE
import androidx.camera.integration.antelope.MainActivity.Companion.cameraParams
import androidx.camera.integration.antelope.MainActivity.Companion.logd
import androidx.camera.integration.antelope.cameracontrollers.Camera2CaptureSessionCallback
import androidx.camera.integration.antelope.cameracontrollers.Camera2DeviceStateCallback
import kotlinx.android.synthetic.main.activity_main.surface_preview
import kotlinx.android.synthetic.main.activity_main.texture_preview
import java.util.Arrays
import java.util.Collections
/** The camera API to use */
enum class CameraAPI(private val api: String) {
/** Camera 1 API */
CAMERA1("Camera1"),
/** Camera 2 API */
CAMERA2("Camera2"),
/** Camera X API */
CAMERAX("CameraX")
}
/** The output capture size to request for captures */
enum class ImageCaptureSize(private val size: String) {
/** Request captures to be the maximum supported size for this camera sensor */
MAX("Max"),
/** Request captures to be the minimum supported size for this camera sensor */
MIN("Min"),
}
/** The focus mechanism to request for captures */
enum class FocusMode(private val mode: String) {
/** Auto-focus */
AUTO("Auto"),
/** Continuous auto-focus */
CONTINUOUS("Continuous"),
/** For fixed-focus lenses */
FIXED("Fixed")
}
/**
* For all the cameras associated with the device, populate the CameraParams values and add them to
* the companion object for the activity.
*/
fun initializeCameras(activity: MainActivity) {
val manager = activity.getSystemService(AppCompatActivity.CAMERA_SERVICE) as CameraManager
try {
val numCameras = manager.cameraIdList.size
for (cameraId in manager.cameraIdList) {
val tempCameraParams = CameraParams().apply {
val cameraChars = manager.getCameraCharacteristics(cameraId)
val cameraCapabilities =
cameraChars.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES)
?: IntArray(0)
// Multi-camera
for (capability in cameraCapabilities) {
if (capability ==
CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA) {
hasMulti = true
} else if (capability ==
CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR) {
hasManualControl = true
}
}
logd("Camera " + cameraId + " of " + numCameras)
id = cameraId
isOpen = false
hasFlash = cameraChars.get(CameraCharacteristics.FLASH_INFO_AVAILABLE) ?: false
isFront = CameraCharacteristics.LENS_FACING_FRONT ==
cameraChars.get(CameraCharacteristics.LENS_FACING)
isExternal = (Build.VERSION.SDK_INT >= 23 &&
CameraCharacteristics.LENS_FACING_EXTERNAL ==
cameraChars.get(CameraCharacteristics.LENS_FACING))
characteristics = cameraChars
focalLengths =
cameraChars.get(CameraCharacteristics.LENS_INFO_AVAILABLE_FOCAL_LENGTHS)
?: FloatArray(0)
smallestFocalLength = smallestFocalLength(focalLengths)
minDeltaFromNormal = focalLengthMinDeltaFromNormal(focalLengths)
apertures = cameraChars.get(CameraCharacteristics.LENS_INFO_AVAILABLE_APERTURES)
?: FloatArray(0)
largestAperture = largestAperture(apertures)
minFocusDistance =
cameraChars.get(CameraCharacteristics.LENS_INFO_MINIMUM_FOCUS_DISTANCE)
?: MainActivity.FIXED_FOCUS_DISTANCE
for (focalLength in focalLengths) {
logd("In " + id + " found focalLength: " + focalLength)
}
logd("Smallest smallestFocalLength: " + smallestFocalLength)
logd("minFocusDistance: " + minFocusDistance)
for (aperture in apertures) {
logd("In " + id + " found aperture: " + aperture)
}
logd("Largest aperture: " + largestAperture)
if (hasManualControl) {
logd("Has Manual, minFocusDistance: " + minFocusDistance)
}
// Autofocus
hasAF = minFocusDistance != FIXED_FOCUS_DISTANCE // If camera is fixed focus, no AF
effects = cameraChars.get(CameraCharacteristics.CONTROL_AVAILABLE_EFFECTS)
?: IntArray(0)
hasSepia = effects.contains(CameraMetadata.CONTROL_EFFECT_MODE_SEPIA)
hasMono = effects.contains(CameraMetadata.CONTROL_EFFECT_MODE_MONO)
if (hasSepia)
logd("WE HAVE Sepia!")
if (hasMono)
logd("WE HAVE Mono!")
isLegacy = cameraChars.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL) ==
CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY
val activeSensorRect: Rect = cameraChars.get(SENSOR_INFO_ACTIVE_ARRAY_SIZE)!!
megapixels = (activeSensorRect.width() * activeSensorRect.height()) / 1000000
camera2DeviceStateCallback =
Camera2DeviceStateCallback(this, activity, TestConfig())
camera2CaptureSessionCallback =
Camera2CaptureSessionCallback(activity, this, TestConfig())
previewSurfaceView = activity.surface_preview
cameraXPreviewTexture = activity.texture_preview
cameraXPreviewBuilder = Preview.Builder()
cameraXCaptureBuilder = ImageCapture.Builder()
imageAvailableListener =
ImageAvailableListener(activity, this, TestConfig())
if (Build.VERSION.SDK_INT >= 28) {
physicalCameras = cameraChars.physicalCameraIds
}
// Get Camera2 and CameraX image capture sizes
val map =
characteristics?.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP)
if (map != null) {
cam2MaxSize = Collections.max(
Arrays.asList(*map.getOutputSizes(ImageFormat.JPEG)),
CompareSizesByArea())
cam2MinSize = Collections.min(
Arrays.asList(*map.getOutputSizes(ImageFormat.JPEG)),
CompareSizesByArea())
// Use minimum image size for preview
previewSurfaceView?.holder?.setFixedSize(cam2MinSize.width, cam2MinSize.height)
}
setupImageReader(activity, this, TestConfig())
}
cameraParams.put(cameraId, tempCameraParams)
} // For all camera devices
} catch (accessError: CameraAccessException) {
accessError.printStackTrace()
}
}
/**
* Convenience method to configure the ImageReaders required for Camera1 and Camera2 APIs.
*
* Uses JPEG image format, checks the current test configuration to determine the needed size.
*/
fun setupImageReader(activity: MainActivity, params: CameraParams, testConfig: TestConfig) {
// Only use ImageReader for Camera1 and Camera2
if (CameraAPI.CAMERAX != testConfig.api) {
params.imageAvailableListener = ImageAvailableListener(activity, params, testConfig)
val useLargest = testConfig.imageCaptureSize == ImageCaptureSize.MAX
val size = if (useLargest)
params.cam2MaxSize
else
params.cam2MinSize
params.imageReader?.close()
params.imageReader = ImageReader.newInstance(
size.width, size.height,
ImageFormat.JPEG, 5
)
params.imageReader?.setOnImageAvailableListener(
params.imageAvailableListener, params.backgroundHandler
)
}
}
/** Finds the smallest focal length in the given array, useful for finding the widest angle lens */
fun smallestFocalLength(focalLengths: FloatArray): Float = focalLengths.minOrNull()
?: MainActivity.INVALID_FOCAL_LENGTH
/** Finds the largest aperture in the array of focal lengths */
fun largestAperture(apertures: FloatArray): Float = apertures.maxOrNull()
?: MainActivity.NO_APERTURE
/** Finds the most "normal" focal length in the array of focal lengths */
fun focalLengthMinDeltaFromNormal(focalLengths: FloatArray): Float =
focalLengths.minByOrNull { Math.abs(it - MainActivity.NORMAL_FOCAL_LENGTH) }
?: Float.MAX_VALUE
/** Adds automatic flash to the given CaptureRequest.Builder */
fun setAutoFlash(params: CameraParams, requestBuilder: CaptureRequest.Builder?) {
try {
if (params.hasFlash) {
requestBuilder?.set(CaptureRequest.CONTROL_AE_MODE,
CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH)
// Force flash always on
// requestBuilder?.set(CaptureRequest.CONTROL_AE_MODE,
// CaptureRequest.CONTROL_AE_MODE_ON_ALWAYS_FLASH)
}
} catch (e: Exception) {
// Do nothing
}
}
/**
* We have to take sensor orientation into account and rotate JPEG properly.
*/
fun getOrientation(params: CameraParams, rotation: Int): Int {
val orientations = SparseIntArray()
orientations.append(Surface.ROTATION_0, 90)
orientations.append(Surface.ROTATION_90, 0)
orientations.append(Surface.ROTATION_180, 270)
orientations.append(Surface.ROTATION_270, 180)
logd("Orientation: sensor: " +
params.characteristics?.get(CameraCharacteristics.SENSOR_ORIENTATION) +
" and current rotation: " + orientations.get(rotation))
val sensorRotation: Int =
params.characteristics?.get(CameraCharacteristics.SENSOR_ORIENTATION) ?: 0
return (orientations.get(rotation) + sensorRotation + 270) % 360
}