fix: Add extra sanity-checks for CaptureRequest (#2571)

* fix: Add extra sanity-checks for `CaptureRequest`

* Update CameraDeviceDetails.kt

* Refactor zoom a bit

* fix: Remove unneeded flash

* Move to AE instead of FLASH control

* Revert "Move to AE instead of FLASH control"

This reverts commit 755689411535803d156a4e84f143d0c9d08c858f.

* Set AE Mode to ON for manual flash control
This commit is contained in:
Marc Rousavy 2024-02-16 13:13:56 +01:00 committed by GitHub
parent 57c6431353
commit b105de0194
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 27 additions and 44 deletions

View File

@ -30,10 +30,12 @@ suspend fun CameraView.takePhoto(optionsMap: ReadableMap): WritableMap {
val qualityPrioritization = options["qualityPrioritization"] as? String ?: "balanced" val qualityPrioritization = options["qualityPrioritization"] as? String ?: "balanced"
val flash = options["flash"] as? String ?: "off" val flash = options["flash"] as? String ?: "off"
val enableAutoRedEyeReduction = options["enableAutoRedEyeReduction"] == true
val enableAutoStabilization = options["enableAutoStabilization"] == true val enableAutoStabilization = options["enableAutoStabilization"] == true
val enableShutterSound = options["enableShutterSound"] as? Boolean ?: true val enableShutterSound = options["enableShutterSound"] as? Boolean ?: true
// TODO: Implement Red Eye Reduction
options["enableAutoRedEyeReduction"]
val flashMode = Flash.fromUnionValue(flash) val flashMode = Flash.fromUnionValue(flash)
val qualityPrioritizationMode = QualityPrioritization.fromUnionValue(qualityPrioritization) val qualityPrioritizationMode = QualityPrioritization.fromUnionValue(qualityPrioritization)
@ -41,7 +43,6 @@ suspend fun CameraView.takePhoto(optionsMap: ReadableMap): WritableMap {
qualityPrioritizationMode, qualityPrioritizationMode,
flashMode, flashMode,
enableShutterSound, enableShutterSound,
enableAutoRedEyeReduction,
enableAutoStabilization, enableAutoStabilization,
orientation orientation
) )

View File

@ -58,7 +58,10 @@ class CameraDeviceDetails(private val cameraManager: CameraManager, val cameraId
val isMultiCam by lazy { capabilities.contains(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA) } val isMultiCam by lazy { capabilities.contains(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA) }
val supportsDepthCapture by lazy { capabilities.contains(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_DEPTH_OUTPUT) } val supportsDepthCapture by lazy { capabilities.contains(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_DEPTH_OUTPUT) }
val supportsRawCapture by lazy { capabilities.contains(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW) } val supportsRawCapture by lazy { capabilities.contains(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW) }
val supportsLowLightBoost by lazy { extensions.contains(CameraExtensionCharacteristics.EXTENSION_NIGHT) } val supportsLowLightBoost by lazy {
extensions.contains(CameraExtensionCharacteristics.EXTENSION_NIGHT) &&
modes.contains(CameraCharacteristics.CONTROL_MODE_USE_SCENE_MODE)
}
val lensFacing by lazy { LensFacing.fromCameraCharacteristics(characteristics) } val lensFacing by lazy { LensFacing.fromCameraCharacteristics(characteristics) }
val hasFlash by lazy { characteristics.get(CameraCharacteristics.FLASH_INFO_AVAILABLE) ?: false } val hasFlash by lazy { characteristics.get(CameraCharacteristics.FLASH_INFO_AVAILABLE) ?: false }
val focalLengths by lazy { val focalLengths by lazy {
@ -122,6 +125,7 @@ class CameraDeviceDetails(private val cameraManager: CameraManager, val cameraId
val supportsExposureRegions by lazy { (characteristics.get(CameraCharacteristics.CONTROL_MAX_REGIONS_AE) ?: 0) > 0 } val supportsExposureRegions by lazy { (characteristics.get(CameraCharacteristics.CONTROL_MAX_REGIONS_AE) ?: 0) > 0 }
val supportsWhiteBalanceRegions by lazy { (characteristics.get(CameraCharacteristics.CONTROL_MAX_REGIONS_AWB) ?: 0) > 0 } val supportsWhiteBalanceRegions by lazy { (characteristics.get(CameraCharacteristics.CONTROL_MAX_REGIONS_AWB) ?: 0) > 0 }
val modes by lazy { characteristics.get(CameraCharacteristics.CONTROL_AVAILABLE_MODES)?.toList() ?: emptyList() }
val afModes by lazy { characteristics.get(CameraCharacteristics.CONTROL_AF_AVAILABLE_MODES)?.toList() ?: emptyList() } val afModes by lazy { characteristics.get(CameraCharacteristics.CONTROL_AF_AVAILABLE_MODES)?.toList() ?: emptyList() }
val aeModes by lazy { characteristics.get(CameraCharacteristics.CONTROL_AE_AVAILABLE_MODES)?.toList() ?: emptyList() } val aeModes by lazy { characteristics.get(CameraCharacteristics.CONTROL_AE_AVAILABLE_MODES)?.toList() ?: emptyList() }
val awbModes by lazy { characteristics.get(CameraCharacteristics.CONTROL_AWB_AVAILABLE_MODES)?.toList() ?: emptyList() } val awbModes by lazy { characteristics.get(CameraCharacteristics.CONTROL_AWB_AVAILABLE_MODES)?.toList() ?: emptyList() }

View File

@ -368,7 +368,6 @@ class CameraSession(private val context: Context, private val cameraManager: Cam
qualityPrioritization: QualityPrioritization, qualityPrioritization: QualityPrioritization,
flash: Flash, flash: Flash,
enableShutterSound: Boolean, enableShutterSound: Boolean,
enableRedEyeReduction: Boolean,
enableAutoStabilization: Boolean, enableAutoStabilization: Boolean,
outputOrientation: Orientation outputOrientation: Orientation
): CapturedPhoto { ): CapturedPhoto {
@ -378,7 +377,6 @@ class CameraSession(private val context: Context, private val cameraManager: Cam
val result = captureSession.capture( val result = captureSession.capture(
qualityPrioritization, qualityPrioritization,
flash, flash,
enableRedEyeReduction,
enableAutoStabilization, enableAutoStabilization,
photoOutput.enableHdr, photoOutput.enableHdr,
outputOrientation, outputOrientation,

View File

@ -140,7 +140,6 @@ class PersistentCameraCaptureSession(private val cameraManager: CameraManager, p
suspend fun capture( suspend fun capture(
qualityPrioritization: QualityPrioritization, qualityPrioritization: QualityPrioritization,
flash: Flash, flash: Flash,
enableRedEyeReduction: Boolean,
enableAutoStabilization: Boolean, enableAutoStabilization: Boolean,
enablePhotoHdr: Boolean, enablePhotoHdr: Boolean,
orientation: Orientation, orientation: Orientation,
@ -157,8 +156,6 @@ class PersistentCameraCaptureSession(private val cameraManager: CameraManager, p
val photoRequest = PhotoCaptureRequest( val photoRequest = PhotoCaptureRequest(
repeatingRequest, repeatingRequest,
qualityPrioritization, qualityPrioritization,
flash,
enableRedEyeReduction,
enableAutoStabilization, enableAutoStabilization,
enablePhotoHdr, enablePhotoHdr,
orientation orientation
@ -195,6 +192,7 @@ class PersistentCameraCaptureSession(private val cameraManager: CameraManager, p
// 2. Once precapture AF/AE/AWB successfully locked, capture the actual photo // 2. Once precapture AF/AE/AWB successfully locked, capture the actual photo
val singleRequest = photoRequest.createCaptureRequest(device, deviceDetails, outputs) val singleRequest = photoRequest.createCaptureRequest(device, deviceDetails, outputs)
if (result.needsFlash) { if (result.needsFlash) {
singleRequest.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON)
singleRequest.set(CaptureRequest.FLASH_MODE, CaptureRequest.FLASH_MODE_SINGLE) singleRequest.set(CaptureRequest.FLASH_MODE, CaptureRequest.FLASH_MODE_SINGLE)
} }
return session.capture(singleRequest.build(), enableShutterSound) return session.capture(singleRequest.build(), enableShutterSound)

View File

@ -75,7 +75,7 @@ abstract class CameraCaptureRequest(
} }
// Set Zoom // Set Zoom
builder.setZoom(zoom, deviceDetails.characteristics) builder.setZoom(zoom, deviceDetails)
// Set Torch // Set Torch
if (torch == Torch.ON) { if (torch == Torch.ON) {

View File

@ -7,7 +7,6 @@ import android.os.Build
import android.util.Log import android.util.Log
import com.mrousavy.camera.core.CameraDeviceDetails import com.mrousavy.camera.core.CameraDeviceDetails
import com.mrousavy.camera.core.outputs.SurfaceOutput import com.mrousavy.camera.core.outputs.SurfaceOutput
import com.mrousavy.camera.types.Flash
import com.mrousavy.camera.types.HardwareLevel import com.mrousavy.camera.types.HardwareLevel
import com.mrousavy.camera.types.Orientation import com.mrousavy.camera.types.Orientation
import com.mrousavy.camera.types.QualityPrioritization import com.mrousavy.camera.types.QualityPrioritization
@ -16,8 +15,6 @@ import com.mrousavy.camera.types.Torch
class PhotoCaptureRequest( class PhotoCaptureRequest(
repeatingRequest: RepeatingCaptureRequest, repeatingRequest: RepeatingCaptureRequest,
private val qualityPrioritization: QualityPrioritization, private val qualityPrioritization: QualityPrioritization,
private val flash: Flash,
private val enableRedEyeReduction: Boolean,
private val enableAutoStabilization: Boolean, private val enableAutoStabilization: Boolean,
enablePhotoHdr: Boolean, enablePhotoHdr: Boolean,
private val outputOrientation: Orientation private val outputOrientation: Orientation
@ -138,26 +135,6 @@ class PhotoCaptureRequest(
val targetOrientation = outputOrientation.toSensorRelativeOrientation(deviceDetails) val targetOrientation = outputOrientation.toSensorRelativeOrientation(deviceDetails)
builder.set(CaptureRequest.JPEG_ORIENTATION, targetOrientation.toDegrees()) builder.set(CaptureRequest.JPEG_ORIENTATION, targetOrientation.toDegrees())
// TODO: Fix flash.
when (flash) {
// Set the Flash Mode
Flash.OFF -> {
builder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON)
builder.set(CaptureRequest.FLASH_MODE, CaptureRequest.FLASH_MODE_OFF)
}
Flash.ON -> {
builder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON)
builder.set(CaptureRequest.FLASH_MODE, CaptureRequest.FLASH_MODE_TORCH)
}
Flash.AUTO -> {
if (enableRedEyeReduction) {
builder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH_REDEYE)
} else {
builder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH)
}
}
}
// Set stabilization for this Frame // Set stabilization for this Frame
if (enableAutoStabilization) { if (enableAutoStabilization) {
if (deviceDetails.opticalStabilizationModes.contains(CameraCharacteristics.LENS_OPTICAL_STABILIZATION_MODE_ON)) { if (deviceDetails.opticalStabilizationModes.contains(CameraCharacteristics.LENS_OPTICAL_STABILIZATION_MODE_ON)) {

View File

@ -11,6 +11,7 @@ import com.mrousavy.camera.core.InvalidVideoStabilizationMode
import com.mrousavy.camera.core.PropRequiresFormatToBeNonNullError import com.mrousavy.camera.core.PropRequiresFormatToBeNonNullError
import com.mrousavy.camera.core.outputs.SurfaceOutput import com.mrousavy.camera.core.outputs.SurfaceOutput
import com.mrousavy.camera.types.CameraDeviceFormat import com.mrousavy.camera.types.CameraDeviceFormat
import com.mrousavy.camera.types.HardwareLevel
import com.mrousavy.camera.types.Torch import com.mrousavy.camera.types.Torch
import com.mrousavy.camera.types.VideoStabilizationMode import com.mrousavy.camera.types.VideoStabilizationMode
@ -51,7 +52,9 @@ class RepeatingCaptureRequest(
): CaptureRequest.Builder { ): CaptureRequest.Builder {
val builder = super.createCaptureRequest(template, device, deviceDetails, outputs) val builder = super.createCaptureRequest(template, device, deviceDetails, outputs)
builder.set(CaptureRequest.CONTROL_MODE, CaptureRequest.CONTROL_MODE_AUTO) if (deviceDetails.modes.contains(CameraCharacteristics.CONTROL_MODE_AUTO)) {
builder.set(CaptureRequest.CONTROL_MODE, CaptureRequest.CONTROL_MODE_AUTO)
}
// Set AF // Set AF
if (enableVideoPipeline && deviceDetails.afModes.contains(CameraCharacteristics.CONTROL_AF_MODE_CONTINUOUS_VIDEO)) { if (enableVideoPipeline && deviceDetails.afModes.contains(CameraCharacteristics.CONTROL_AF_MODE_CONTINUOUS_VIDEO)) {
@ -95,7 +98,11 @@ class RepeatingCaptureRequest(
builder.set(CaptureRequest.CONTROL_VIDEO_STABILIZATION_MODE, getBestDigitalStabilizationMode(deviceDetails)) builder.set(CaptureRequest.CONTROL_VIDEO_STABILIZATION_MODE, getBestDigitalStabilizationMode(deviceDetails))
} }
VideoStabilizationMode.CINEMATIC, VideoStabilizationMode.CINEMATIC_EXTENDED -> { VideoStabilizationMode.CINEMATIC, VideoStabilizationMode.CINEMATIC_EXTENDED -> {
builder.set(CaptureRequest.LENS_OPTICAL_STABILIZATION_MODE, CaptureRequest.LENS_OPTICAL_STABILIZATION_MODE_ON) if (deviceDetails.hardwareLevel.isAtLeast(HardwareLevel.LIMITED)) {
builder.set(CaptureRequest.LENS_OPTICAL_STABILIZATION_MODE, CaptureRequest.LENS_OPTICAL_STABILIZATION_MODE_ON)
} else {
builder.set(CaptureRequest.CONTROL_VIDEO_STABILIZATION_MODE, getBestDigitalStabilizationMode(deviceDetails))
}
} }
else -> throw InvalidVideoStabilizationMode(videoStabilizationMode) else -> throw InvalidVideoStabilizationMode(videoStabilizationMode)
} }

View File

@ -1,20 +1,18 @@
package com.mrousavy.camera.extensions package com.mrousavy.camera.extensions
import android.hardware.camera2.CameraCharacteristics
import android.hardware.camera2.CaptureRequest import android.hardware.camera2.CaptureRequest
import android.os.Build import android.os.Build
import android.util.Range import com.mrousavy.camera.core.CameraDeviceDetails
import com.mrousavy.camera.types.HardwareLevel
fun CaptureRequest.Builder.setZoom(zoom: Float, cameraCharacteristics: CameraCharacteristics) { fun CaptureRequest.Builder.setZoom(zoom: Float, deviceDetails: CameraDeviceDetails) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { val zoomRange = deviceDetails.zoomRange
val zoomRange = cameraCharacteristics.get(CameraCharacteristics.CONTROL_ZOOM_RATIO_RANGE) ?: Range(1f, 1f) val zoomClamped = zoomRange.clamp(zoom)
val zoomClamped = zoomRange.clamp(zoom)
if (deviceDetails.hardwareLevel.isAtLeast(HardwareLevel.LIMITED) && Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
this.set(CaptureRequest.CONTROL_ZOOM_RATIO, zoomClamped) this.set(CaptureRequest.CONTROL_ZOOM_RATIO, zoomClamped)
} else { } else {
val maxZoom = cameraCharacteristics.get(CameraCharacteristics.SCALER_AVAILABLE_MAX_DIGITAL_ZOOM) val size = deviceDetails.activeSize
val zoomRange = Range(1f, maxZoom ?: 1f)
val size = cameraCharacteristics.get(CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE)!!
val zoomClamped = zoomRange.clamp(zoom)
this.set(CaptureRequest.SCALER_CROP_REGION, size.zoomed(zoomClamped)) this.set(CaptureRequest.SCALER_CROP_REGION, size.zoomed(zoomClamped))
} }
} }