use crate::ActiveBackend;
use crate::backend::BackendControls;
use crate::error::Error;
use crate::types::Device;
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum PowerLineFrequency {
Disabled,
Hz50,
Hz60,
Auto,
}
#[derive(Clone, Debug, Default, PartialEq)]
pub struct Controls {
pub focus: Option<f32>,
pub auto_focus: Option<bool>,
pub exposure: Option<f32>,
pub auto_exposure: Option<bool>,
pub white_balance_temperature: Option<f32>,
pub auto_white_balance: Option<bool>,
pub brightness: Option<f32>,
pub contrast: Option<f32>,
pub saturation: Option<f32>,
pub sharpness: Option<f32>,
pub gain: Option<f32>,
pub backlight_compensation: Option<f32>,
pub power_line_frequency: Option<PowerLineFrequency>,
pub pan: Option<f32>,
pub tilt: Option<f32>,
pub zoom: Option<f32>,
}
#[derive(Copy, Clone, Debug, PartialEq)]
#[non_exhaustive]
pub struct ControlRange {
pub min: f32,
pub max: f32,
pub step: f32,
pub default: f32,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub struct PowerLineFrequencyCapability {
pub hz50: bool,
pub hz60: bool,
pub disabled: bool,
pub auto: bool,
pub default: PowerLineFrequency,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum ControlKind {
Focus,
AutoFocus,
Exposure,
AutoExposure,
WhiteBalanceTemperature,
AutoWhiteBalance,
Brightness,
Contrast,
Saturation,
Sharpness,
Gain,
BacklightCompensation,
PowerLineFrequency,
Pan,
Tilt,
Zoom,
}
impl ControlKind {
pub const ALL: [ControlKind; 16] = [
ControlKind::Focus,
ControlKind::AutoFocus,
ControlKind::Exposure,
ControlKind::AutoExposure,
ControlKind::WhiteBalanceTemperature,
ControlKind::AutoWhiteBalance,
ControlKind::Brightness,
ControlKind::Contrast,
ControlKind::Saturation,
ControlKind::Sharpness,
ControlKind::Gain,
ControlKind::BacklightCompensation,
ControlKind::PowerLineFrequency,
ControlKind::Pan,
ControlKind::Tilt,
ControlKind::Zoom,
];
pub fn label(&self) -> &'static str {
match self {
ControlKind::Focus => "focus",
ControlKind::AutoFocus => "auto_focus",
ControlKind::Exposure => "exposure",
ControlKind::AutoExposure => "auto_exposure",
ControlKind::WhiteBalanceTemperature => "white_balance_temperature",
ControlKind::AutoWhiteBalance => "auto_white_balance",
ControlKind::Brightness => "brightness",
ControlKind::Contrast => "contrast",
ControlKind::Saturation => "saturation",
ControlKind::Sharpness => "sharpness",
ControlKind::Gain => "gain",
ControlKind::BacklightCompensation => "backlight_compensation",
ControlKind::PowerLineFrequency => "power_line_frequency",
ControlKind::Pan => "pan",
ControlKind::Tilt => "tilt",
ControlKind::Zoom => "zoom",
}
}
pub fn caveat(&self) -> Option<&'static str> {
#[cfg(target_os = "macos")]
{
match self {
ControlKind::Brightness
| ControlKind::Contrast
| ControlKind::Saturation
| ControlKind::Sharpness
| ControlKind::BacklightCompensation => Some(
"macOS: AVFoundation doesn't expose per-channel image-processing controls. \
Apply CPU/GPU post-processing (shaders, color matrices) over the Frame \
bytes in your app. The library is capture-only.",
),
ControlKind::PowerLineFrequency => {
Some("macOS: AVFoundation doesn't expose AC mains frequency filtering.")
}
ControlKind::Pan | ControlKind::Tilt => Some(
"macOS: AVFoundation doesn't expose pan/tilt controls for built-in or UVC cameras.",
),
ControlKind::Focus
| ControlKind::AutoFocus
| ControlKind::Exposure
| ControlKind::AutoExposure
| ControlKind::WhiteBalanceTemperature
| ControlKind::AutoWhiteBalance
| ControlKind::Gain
| ControlKind::Zoom => None,
}
}
#[cfg(not(target_os = "macos"))]
{
None
}
}
}
#[derive(Clone, Debug, Default, PartialEq)]
#[non_exhaustive]
pub struct ControlCapabilities {
pub focus: Option<ControlRange>,
pub auto_focus: Option<bool>,
pub exposure: Option<ControlRange>,
pub auto_exposure: Option<bool>,
pub white_balance_temperature: Option<ControlRange>,
pub auto_white_balance: Option<bool>,
pub brightness: Option<ControlRange>,
pub contrast: Option<ControlRange>,
pub saturation: Option<ControlRange>,
pub sharpness: Option<ControlRange>,
pub gain: Option<ControlRange>,
pub backlight_compensation: Option<ControlRange>,
pub power_line_frequency: Option<PowerLineFrequencyCapability>,
pub pan: Option<ControlRange>,
pub tilt: Option<ControlRange>,
pub zoom: Option<ControlRange>,
}
pub fn control_capabilities(device: &Device) -> Result<ControlCapabilities, Error> {
<ActiveBackend as BackendControls>::control_capabilities(&device.id)
}
pub fn read_controls(device: &Device) -> Result<Controls, Error> {
<ActiveBackend as BackendControls>::read_controls(&device.id)
}
pub fn apply_controls(device: &Device, controls: &Controls) -> Result<(), Error> {
<ActiveBackend as BackendControls>::apply_controls(&device.id, controls)
}
pub fn default_controls(capabilities: &ControlCapabilities) -> Controls {
let auto_toggle = |supported: Option<bool>| match supported {
Some(true) => Some(true),
_ => None,
};
let numeric_with_auto_fallback = |range: Option<&ControlRange>, auto: Option<bool>| {
if auto == Some(true) {
None
} else {
range.map(|range| range.default)
}
};
Controls {
focus: numeric_with_auto_fallback(capabilities.focus.as_ref(), capabilities.auto_focus),
auto_focus: auto_toggle(capabilities.auto_focus),
exposure: numeric_with_auto_fallback(
capabilities.exposure.as_ref(),
capabilities.auto_exposure,
),
auto_exposure: auto_toggle(capabilities.auto_exposure),
white_balance_temperature: numeric_with_auto_fallback(
capabilities.white_balance_temperature.as_ref(),
capabilities.auto_white_balance,
),
auto_white_balance: auto_toggle(capabilities.auto_white_balance),
brightness: capabilities.brightness.as_ref().map(|range| range.default),
contrast: capabilities.contrast.as_ref().map(|range| range.default),
saturation: capabilities.saturation.as_ref().map(|range| range.default),
sharpness: capabilities.sharpness.as_ref().map(|range| range.default),
gain: capabilities.gain.as_ref().map(|range| range.default),
backlight_compensation: capabilities
.backlight_compensation
.as_ref()
.map(|range| range.default),
power_line_frequency: capabilities
.power_line_frequency
.as_ref()
.map(|capability| capability.default),
pan: capabilities.pan.as_ref().map(|range| range.default),
tilt: capabilities.tilt.as_ref().map(|range| range.default),
zoom: capabilities.zoom.as_ref().map(|range| range.default),
}
}
pub fn reset_to_defaults(device: &Device) -> Result<(), Error> {
let capabilities = control_capabilities(device)?;
let controls = default_controls(&capabilities);
apply_controls(device, &controls)
}