fix: Validate input props (fps
, hdr
, torch
, ...) instead of silently crashing (#2354)
* fix: Fix Blackscreen by deterministically destroying session if `isActive=false` * Re-open Camera if session died * Simplify Camera * Disconnect is optional, block when resetting state * fix: Log in `configure { ... }` * fix: Make concurrent configure safe * fix: Don't resize preview * fix: Use current `CameraConfiguration` * Don't start if no outputs are available * Only mount with preview outputs * Update CameraSession.kt * Update PreviewView.kt * Better logging * Update CameraSession.kt * Extract * fix: Rebuild entire session if `isActive` changed * isActive safe * Start session at 1 * Create ActiveCameraDevice.kt * interrupts * chore: Freeze `frame` in `useFrameProcessor` * Revert "chore: Freeze `frame` in `useFrameProcessor`" This reverts commit dff93d506e29a791d8dea8842b880ab5c892211e. * chore: Better logging * fix: Move HDR to `video`/`photo` config * fix: Fix hdr usage * fix: Ignore any updates after destroying Camera * fix: Fix video HDR * chore: Format code * fix: Check Camera permission * Remove unneeded error * Update CameraSession.kt * Update CameraPage.tsx * Delete OutputConfiguration.toDebugString.kt * Update CameraSession.kt * fix: Perform sanity checks to make sure props are valid * format
This commit is contained in:
parent
0d21bc3a57
commit
cc60ad296a
@ -23,26 +23,26 @@ import com.mrousavy.camera.types.VideoStabilizationMode
|
|||||||
import kotlin.math.atan2
|
import kotlin.math.atan2
|
||||||
import kotlin.math.sqrt
|
import kotlin.math.sqrt
|
||||||
|
|
||||||
class CameraDeviceDetails(private val cameraManager: CameraManager, private val cameraId: String) {
|
class CameraDeviceDetails(val cameraManager: CameraManager, val cameraId: String) {
|
||||||
private val characteristics = cameraManager.getCameraCharacteristics(cameraId)
|
val characteristics = cameraManager.getCameraCharacteristics(cameraId)
|
||||||
private val hardwareLevel = HardwareLevel.fromCameraCharacteristics(characteristics)
|
val hardwareLevel = HardwareLevel.fromCameraCharacteristics(characteristics)
|
||||||
private val capabilities = characteristics.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES) ?: IntArray(0)
|
val capabilities = characteristics.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES) ?: IntArray(0)
|
||||||
private val extensions = getSupportedExtensions()
|
val extensions = getSupportedExtensions()
|
||||||
|
|
||||||
// device characteristics
|
// device characteristics
|
||||||
private val isMultiCam = capabilities.contains(11) // TODO: CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA
|
val isMultiCam = capabilities.contains(11) // TODO: CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA
|
||||||
private val supportsDepthCapture = capabilities.contains(8) // TODO: CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_DEPTH_OUTPUT
|
val supportsDepthCapture = capabilities.contains(8) // TODO: CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_DEPTH_OUTPUT
|
||||||
private val supportsRawCapture = capabilities.contains(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW)
|
val supportsRawCapture = capabilities.contains(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW)
|
||||||
private val supportsLowLightBoost = extensions.contains(4) // TODO: CameraExtensionCharacteristics.EXTENSION_NIGHT
|
val supportsLowLightBoost = extensions.contains(4) // TODO: CameraExtensionCharacteristics.EXTENSION_NIGHT
|
||||||
private val lensFacing = LensFacing.fromCameraCharacteristics(characteristics)
|
val lensFacing = LensFacing.fromCameraCharacteristics(characteristics)
|
||||||
private val hasFlash = characteristics.get(CameraCharacteristics.FLASH_INFO_AVAILABLE) ?: false
|
val hasFlash = characteristics.get(CameraCharacteristics.FLASH_INFO_AVAILABLE) ?: false
|
||||||
private val focalLengths =
|
val focalLengths =
|
||||||
characteristics.get(CameraCharacteristics.LENS_INFO_AVAILABLE_FOCAL_LENGTHS)
|
characteristics.get(CameraCharacteristics.LENS_INFO_AVAILABLE_FOCAL_LENGTHS)
|
||||||
// 35mm is the film standard sensor size
|
// 35mm is the film standard sensor size
|
||||||
?: floatArrayOf(35f)
|
?: floatArrayOf(35f)
|
||||||
private val sensorSize = characteristics.get(CameraCharacteristics.SENSOR_INFO_PHYSICAL_SIZE)!!
|
val sensorSize = characteristics.get(CameraCharacteristics.SENSOR_INFO_PHYSICAL_SIZE)!!
|
||||||
private val sensorOrientation = characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION)!!
|
val sensorOrientation = characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION)!!
|
||||||
private val name = (
|
val name = (
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||||
characteristics.get(CameraCharacteristics.INFO_VERSION)
|
characteristics.get(CameraCharacteristics.INFO_VERSION)
|
||||||
} else {
|
} else {
|
||||||
@ -51,32 +51,32 @@ class CameraDeviceDetails(private val cameraManager: CameraManager, private val
|
|||||||
) ?: "$lensFacing ($cameraId)"
|
) ?: "$lensFacing ($cameraId)"
|
||||||
|
|
||||||
// "formats" (all possible configurations for this device)
|
// "formats" (all possible configurations for this device)
|
||||||
private val zoomRange = (
|
val zoomRange = (
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||||
characteristics.get(CameraCharacteristics.CONTROL_ZOOM_RATIO_RANGE)
|
characteristics.get(CameraCharacteristics.CONTROL_ZOOM_RATIO_RANGE)
|
||||||
} else {
|
} else {
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
) ?: Range(1f, characteristics.get(CameraCharacteristics.SCALER_AVAILABLE_MAX_DIGITAL_ZOOM) ?: 1f)
|
) ?: Range(1f, characteristics.get(CameraCharacteristics.SCALER_AVAILABLE_MAX_DIGITAL_ZOOM) ?: 1f)
|
||||||
private val physicalDevices = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P && characteristics.physicalCameraIds.isNotEmpty()) {
|
val physicalDevices = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P && characteristics.physicalCameraIds.isNotEmpty()) {
|
||||||
characteristics.physicalCameraIds
|
characteristics.physicalCameraIds
|
||||||
} else {
|
} else {
|
||||||
setOf(cameraId)
|
setOf(cameraId)
|
||||||
}
|
}
|
||||||
private val minZoom = zoomRange.lower.toDouble()
|
val minZoom = zoomRange.lower.toDouble()
|
||||||
private val maxZoom = zoomRange.upper.toDouble()
|
val maxZoom = zoomRange.upper.toDouble()
|
||||||
|
|
||||||
private val cameraConfig = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP)!!
|
val cameraConfig = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP)!!
|
||||||
private val isoRange = characteristics.get(CameraCharacteristics.SENSOR_INFO_SENSITIVITY_RANGE) ?: Range(0, 0)
|
val isoRange = characteristics.get(CameraCharacteristics.SENSOR_INFO_SENSITIVITY_RANGE) ?: Range(0, 0)
|
||||||
private val exposureRange = characteristics.get(CameraCharacteristics.CONTROL_AE_COMPENSATION_RANGE) ?: Range(0, 0)
|
val exposureRange = characteristics.get(CameraCharacteristics.CONTROL_AE_COMPENSATION_RANGE) ?: Range(0, 0)
|
||||||
private val digitalStabilizationModes =
|
val digitalStabilizationModes =
|
||||||
characteristics.get(CameraCharacteristics.CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES) ?: IntArray(0)
|
characteristics.get(CameraCharacteristics.CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES) ?: IntArray(0)
|
||||||
private val opticalStabilizationModes =
|
val opticalStabilizationModes =
|
||||||
characteristics.get(CameraCharacteristics.LENS_INFO_AVAILABLE_OPTICAL_STABILIZATION) ?: IntArray(0)
|
characteristics.get(CameraCharacteristics.LENS_INFO_AVAILABLE_OPTICAL_STABILIZATION) ?: IntArray(0)
|
||||||
private val supportsPhotoHdr = extensions.contains(3) // TODO: CameraExtensionCharacteristics.EXTENSION_HDR
|
val supportsPhotoHdr = extensions.contains(3) // TODO: CameraExtensionCharacteristics.EXTENSION_HDR
|
||||||
private val supportsVideoHdr = getHasVideoHdr()
|
val supportsVideoHdr = getHasVideoHdr()
|
||||||
|
|
||||||
private val videoFormat = ImageFormat.YUV_420_888
|
val videoFormat = ImageFormat.YUV_420_888
|
||||||
|
|
||||||
// get extensions (HDR, Night Mode, ..)
|
// get extensions (HDR, Night Mode, ..)
|
||||||
private fun getSupportedExtensions(): List<Int> =
|
private fun getSupportedExtensions(): List<Int> =
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package com.mrousavy.camera.core
|
package com.mrousavy.camera.core
|
||||||
|
|
||||||
import com.mrousavy.camera.types.CameraDeviceError
|
import com.mrousavy.camera.types.CameraDeviceError
|
||||||
|
import com.mrousavy.camera.types.VideoStabilizationMode
|
||||||
|
|
||||||
abstract class CameraError(
|
abstract class CameraError(
|
||||||
/**
|
/**
|
||||||
@ -49,6 +50,18 @@ class NoCameraDeviceError :
|
|||||||
)
|
)
|
||||||
class PixelFormatNotSupportedError(format: String) :
|
class PixelFormatNotSupportedError(format: String) :
|
||||||
CameraError("device", "pixel-format-not-supported", "The pixelFormat $format is not supported on the given Camera Device!")
|
CameraError("device", "pixel-format-not-supported", "The pixelFormat $format is not supported on the given Camera Device!")
|
||||||
|
class LowLightBoostNotSupportedError :
|
||||||
|
CameraError(
|
||||||
|
"device",
|
||||||
|
"low-light-boost-not-supported",
|
||||||
|
"The currently selected camera device does not support low-light boost! Select a device where `device.supportsLowLightBoost` is true."
|
||||||
|
)
|
||||||
|
class FlashUnavailableError :
|
||||||
|
CameraError(
|
||||||
|
"device",
|
||||||
|
"flash-unavailable",
|
||||||
|
"The Camera Device does not have a flash unit! Make sure you select a device where `device.hasFlash`/`device.hasTorch` is true."
|
||||||
|
)
|
||||||
|
|
||||||
class CameraNotReadyError :
|
class CameraNotReadyError :
|
||||||
CameraError("session", "camera-not-ready", "The Camera is not ready yet! Wait for the onInitialized() callback!")
|
CameraError("session", "camera-not-ready", "The Camera is not ready yet! Wait for the onInitialized() callback!")
|
||||||
@ -59,6 +72,28 @@ class CameraSessionCannotBeConfiguredError(cameraId: String) :
|
|||||||
class CameraDisconnectedError(cameraId: String, error: CameraDeviceError) :
|
class CameraDisconnectedError(cameraId: String, error: CameraDeviceError) :
|
||||||
CameraError("session", "camera-has-been-disconnected", "The given Camera device (id: $cameraId) has been disconnected! Error: $error")
|
CameraError("session", "camera-has-been-disconnected", "The given Camera device (id: $cameraId) has been disconnected! Error: $error")
|
||||||
|
|
||||||
|
class PropRequiresFormatToBeNonNullError(propName: String) :
|
||||||
|
CameraError("format", "format-required", "The prop \"$propName\" requires a format to be set, but format was null!")
|
||||||
|
class InvalidFpsError(fps: Int) :
|
||||||
|
CameraError(
|
||||||
|
"format",
|
||||||
|
"invalid-fps",
|
||||||
|
"The given format cannot run at $fps FPS! Make sure your FPS is lower than `format.maxFps` but higher than `format.minFps`."
|
||||||
|
)
|
||||||
|
class InvalidVideoStabilizationMode(mode: VideoStabilizationMode) :
|
||||||
|
CameraError(
|
||||||
|
"format",
|
||||||
|
"invalid-video-stabilization-mode",
|
||||||
|
"The given format does not support the videoStabilizationMode \"${mode.unionValue}\"! " +
|
||||||
|
"Select a format that contains ${mode.unionValue} in `format.supportedVideoStabilizationModes`."
|
||||||
|
)
|
||||||
|
class InvalidVideoHdrError :
|
||||||
|
CameraError(
|
||||||
|
"format",
|
||||||
|
"invalid-video-hdr",
|
||||||
|
"The given format does not support videoHdr! Select a format where `format.supportsVideoHdr` is true."
|
||||||
|
)
|
||||||
|
|
||||||
class VideoNotEnabledError :
|
class VideoNotEnabledError :
|
||||||
CameraError("capture", "video-not-enabled", "Video capture is disabled! Pass `video={true}` to enable video recordings.")
|
CameraError("capture", "video-not-enabled", "Video capture is disabled! Pass `video={true}` to enable video recordings.")
|
||||||
class PhotoNotEnabledError :
|
class PhotoNotEnabledError :
|
||||||
|
@ -68,8 +68,13 @@ class CameraSession(private val context: Context, private val cameraManager: Cam
|
|||||||
private var configuration: CameraConfiguration? = null
|
private var configuration: CameraConfiguration? = null
|
||||||
|
|
||||||
// Camera State
|
// Camera State
|
||||||
private var captureSession: CameraCaptureSession? = null
|
|
||||||
private var cameraDevice: CameraDevice? = null
|
private var cameraDevice: CameraDevice? = null
|
||||||
|
set(value) {
|
||||||
|
field = value
|
||||||
|
cameraDeviceDetails = if (value != null) CameraDeviceDetails(cameraManager, value.id) else null
|
||||||
|
}
|
||||||
|
private var cameraDeviceDetails: CameraDeviceDetails? = null
|
||||||
|
private var captureSession: CameraCaptureSession? = null
|
||||||
private var previewRequest: CaptureRequest.Builder? = null
|
private var previewRequest: CaptureRequest.Builder? = null
|
||||||
private var photoOutput: PhotoOutput? = null
|
private var photoOutput: PhotoOutput? = null
|
||||||
private var videoOutput: VideoPipelineOutput? = null
|
private var videoOutput: VideoPipelineOutput? = null
|
||||||
@ -430,27 +435,38 @@ class CameraSession(private val context: Context, private val cameraManager: Cam
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun createRepeatingRequest(device: CameraDevice, targets: List<Surface>, config: CameraConfiguration): CaptureRequest {
|
private fun createRepeatingRequest(device: CameraDevice, targets: List<Surface>, config: CameraConfiguration): CaptureRequest {
|
||||||
val cameraCharacteristics = cameraManager.getCameraCharacteristics(device.id)
|
val deviceDetails = cameraDeviceDetails ?: CameraDeviceDetails(cameraManager, device.id)
|
||||||
|
|
||||||
val template = if (config.video.isEnabled) CameraDevice.TEMPLATE_RECORD else CameraDevice.TEMPLATE_PREVIEW
|
val template = if (config.video.isEnabled) CameraDevice.TEMPLATE_RECORD else CameraDevice.TEMPLATE_PREVIEW
|
||||||
val captureRequest = device.createCaptureRequest(template)
|
val captureRequest = device.createCaptureRequest(template)
|
||||||
|
|
||||||
targets.forEach { t -> captureRequest.addTarget(t) }
|
targets.forEach { t -> captureRequest.addTarget(t) }
|
||||||
|
|
||||||
|
val format = config.format
|
||||||
|
|
||||||
// Set FPS
|
// Set FPS
|
||||||
// TODO: Check if the FPS range is actually supported in the current configuration.
|
|
||||||
val fps = config.fps
|
val fps = config.fps
|
||||||
if (fps != null) {
|
if (fps != null) {
|
||||||
|
if (format == null) throw PropRequiresFormatToBeNonNullError("fps")
|
||||||
|
if (format.maxFps < fps) throw InvalidFpsError(fps)
|
||||||
captureRequest.set(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE, Range(fps, fps))
|
captureRequest.set(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE, Range(fps, fps))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set Video Stabilization
|
// Set Video Stabilization
|
||||||
|
if (config.videoStabilizationMode != VideoStabilizationMode.OFF) {
|
||||||
|
if (format == null) throw PropRequiresFormatToBeNonNullError("videoStabilizationMode")
|
||||||
|
if (!format.videoStabilizationModes.contains(
|
||||||
|
config.videoStabilizationMode
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
throw InvalidVideoStabilizationMode(config.videoStabilizationMode)
|
||||||
|
}
|
||||||
|
}
|
||||||
when (config.videoStabilizationMode) {
|
when (config.videoStabilizationMode) {
|
||||||
VideoStabilizationMode.OFF -> {
|
VideoStabilizationMode.OFF -> {
|
||||||
// do nothing
|
// do nothing
|
||||||
}
|
}
|
||||||
VideoStabilizationMode.STANDARD -> {
|
VideoStabilizationMode.STANDARD -> {
|
||||||
// TODO: Check if that stabilization mode is even supported
|
|
||||||
val mode = if (Build.VERSION.SDK_INT >=
|
val mode = if (Build.VERSION.SDK_INT >=
|
||||||
Build.VERSION_CODES.TIRAMISU
|
Build.VERSION_CODES.TIRAMISU
|
||||||
) {
|
) {
|
||||||
@ -461,35 +477,37 @@ class CameraSession(private val context: Context, private val cameraManager: Cam
|
|||||||
captureRequest.set(CaptureRequest.CONTROL_VIDEO_STABILIZATION_MODE, mode)
|
captureRequest.set(CaptureRequest.CONTROL_VIDEO_STABILIZATION_MODE, mode)
|
||||||
}
|
}
|
||||||
VideoStabilizationMode.CINEMATIC, VideoStabilizationMode.CINEMATIC_EXTENDED -> {
|
VideoStabilizationMode.CINEMATIC, VideoStabilizationMode.CINEMATIC_EXTENDED -> {
|
||||||
// TODO: Check if that stabilization mode is even supported
|
|
||||||
captureRequest.set(CaptureRequest.LENS_OPTICAL_STABILIZATION_MODE, CaptureRequest.LENS_OPTICAL_STABILIZATION_MODE_ON)
|
captureRequest.set(CaptureRequest.LENS_OPTICAL_STABILIZATION_MODE, CaptureRequest.LENS_OPTICAL_STABILIZATION_MODE_ON)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set HDR
|
// Set HDR
|
||||||
// TODO: Check if that value is even supported
|
|
||||||
val video = config.video as? CameraConfiguration.Output.Enabled<CameraConfiguration.Video>
|
val video = config.video as? CameraConfiguration.Output.Enabled<CameraConfiguration.Video>
|
||||||
val videoHdr = video?.config?.enableHdr
|
val videoHdr = video?.config?.enableHdr
|
||||||
if (videoHdr == true) {
|
if (videoHdr == true) {
|
||||||
|
if (format == null) throw PropRequiresFormatToBeNonNullError("videoHdr")
|
||||||
|
if (!format.supportsVideoHdr) throw InvalidVideoHdrError()
|
||||||
captureRequest.set(CaptureRequest.CONTROL_SCENE_MODE, CaptureRequest.CONTROL_SCENE_MODE_HDR)
|
captureRequest.set(CaptureRequest.CONTROL_SCENE_MODE, CaptureRequest.CONTROL_SCENE_MODE_HDR)
|
||||||
} else if (config.enableLowLightBoost) {
|
} else if (config.enableLowLightBoost) {
|
||||||
|
if (!deviceDetails.supportsLowLightBoost) throw LowLightBoostNotSupportedError()
|
||||||
captureRequest.set(CaptureRequest.CONTROL_SCENE_MODE, CaptureRequest.CONTROL_SCENE_MODE_NIGHT)
|
captureRequest.set(CaptureRequest.CONTROL_SCENE_MODE, CaptureRequest.CONTROL_SCENE_MODE_NIGHT)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set Exposure Bias
|
// Set Exposure Bias
|
||||||
// TODO: Check if that exposure value is even supported
|
|
||||||
val exposure = config.exposure?.toInt()
|
val exposure = config.exposure?.toInt()
|
||||||
if (exposure != null) {
|
if (exposure != null) {
|
||||||
captureRequest.set(CaptureRequest.CONTROL_AE_EXPOSURE_COMPENSATION, exposure)
|
val clamped = deviceDetails.exposureRange.clamp(exposure)
|
||||||
|
captureRequest.set(CaptureRequest.CONTROL_AE_EXPOSURE_COMPENSATION, clamped)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set Zoom
|
// Set Zoom
|
||||||
// TODO: Check if that zoom value is even supported
|
// TODO: Cache camera characteristics? Check perf.
|
||||||
|
val cameraCharacteristics = cameraManager.getCameraCharacteristics(device.id)
|
||||||
captureRequest.setZoom(config.zoom, cameraCharacteristics)
|
captureRequest.setZoom(config.zoom, cameraCharacteristics)
|
||||||
|
|
||||||
// Set Torch
|
// Set Torch
|
||||||
// TODO: Check if torch is even supported
|
|
||||||
if (config.torch == Torch.ON) {
|
if (config.torch == Torch.ON) {
|
||||||
|
if (!deviceDetails.hasFlash) throw FlashUnavailableError()
|
||||||
captureRequest.set(CaptureRequest.FLASH_MODE, CaptureRequest.FLASH_MODE_TORCH)
|
captureRequest.set(CaptureRequest.FLASH_MODE, CaptureRequest.FLASH_MODE_TORCH)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -89,9 +89,9 @@ enum DeviceError: String {
|
|||||||
case .invalid:
|
case .invalid:
|
||||||
return "The given Camera device was invalid. Use `useCameraDevice(..)` or `Camera.getAvailableCameraDevices()` to select a suitable Camera device."
|
return "The given Camera device was invalid. Use `useCameraDevice(..)` or `Camera.getAvailableCameraDevices()` to select a suitable Camera device."
|
||||||
case .flashUnavailable:
|
case .flashUnavailable:
|
||||||
return "The Camera Device does not have a flash unit! Make sure you select a device where `hasFlash`/`hasTorch` is true!"
|
return "The Camera Device does not have a flash unit! Select a device where `device.hasFlash`/`device.hasTorch` is true."
|
||||||
case .lowLightBoostNotSupported:
|
case .lowLightBoostNotSupported:
|
||||||
return "The currently selected camera device does not support low-light boost! Make sure you select a device where `supportsLowLightBoost` is true!"
|
return "The currently selected camera device does not support low-light boost! Select a device where `device.supportsLowLightBoost` is true."
|
||||||
case .focusNotSupported:
|
case .focusNotSupported:
|
||||||
return "The currently selected camera device does not support focussing!"
|
return "The currently selected camera device does not support focussing!"
|
||||||
case .microphoneUnavailable:
|
case .microphoneUnavailable:
|
||||||
|
@ -16,8 +16,10 @@ export type DeviceError =
|
|||||||
export type FormatError =
|
export type FormatError =
|
||||||
| 'format/invalid-fps'
|
| 'format/invalid-fps'
|
||||||
| 'format/invalid-video-hdr'
|
| 'format/invalid-video-hdr'
|
||||||
|
| 'format/invalid-video-stabilization-mode'
|
||||||
| 'format/incompatible-pixel-format-with-hdr-setting'
|
| 'format/incompatible-pixel-format-with-hdr-setting'
|
||||||
| 'format/invalid-format'
|
| 'format/invalid-format'
|
||||||
|
| 'format/format-required'
|
||||||
export type SessionError =
|
export type SessionError =
|
||||||
| 'session/camera-not-ready'
|
| 'session/camera-not-ready'
|
||||||
| 'session/camera-cannot-be-opened'
|
| 'session/camera-cannot-be-opened'
|
||||||
|
Loading…
Reference in New Issue
Block a user