mirror of
https://github.com/l1npengtul/nokhwa.git
synced 2026-07-04 02:27:26 +00:00
fix glaring API flaw with RequestedFormat, add control detection for AVFoundation
This commit is contained in:
@@ -29,6 +29,7 @@ extern crate objc;
|
||||
pub mod core_media {
|
||||
// all of this is stolen from bindgen
|
||||
// steal it idc
|
||||
use crate::CGFloat;
|
||||
use core_media_sys::{
|
||||
CMBlockBufferRef, CMFormatDescriptionRef, CMSampleBufferRef, CMTime, CMVideoDimensions,
|
||||
FourCharCode,
|
||||
@@ -120,11 +121,28 @@ pub mod core_media {
|
||||
pub fn CVPixelBufferGetPixelFormatType(pixelBuffer: CVPixelBufferRef) -> OSType;
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Debug, PartialEq, PartialOrd)]
|
||||
pub struct CGPoint {
|
||||
pub x: CGFloat,
|
||||
pub y: CGFloat,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct __CVBuffer {
|
||||
_unused: [u8; 0],
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
#[derive(Copy, Clone, Debug, PartialOrd, PartialEq)]
|
||||
#[repr(C)]
|
||||
pub struct AVCaptureWhiteBalanceGains {
|
||||
pub blueGain: f32,
|
||||
pub greenGain: f32,
|
||||
pub redGain: f32,
|
||||
}
|
||||
|
||||
pub type CVBufferRef = *mut __CVBuffer;
|
||||
|
||||
pub type CVImageBufferRef = CVBufferRef;
|
||||
@@ -166,14 +184,20 @@ pub mod core_media {
|
||||
pub static AVMediaTypeMuxed: AVMediaType;
|
||||
pub static AVMediaTypeMetadataObject: AVMediaType;
|
||||
pub static AVMediaTypeDepthData: AVMediaType;
|
||||
|
||||
pub static AVCaptureLensPositionCurrent: f32;
|
||||
pub static AVCaptureExposureTargetBiasCurrent: f32;
|
||||
pub static AVCaptureExposureDurationCurrent: CMTime;
|
||||
pub static AVCaptureISOCurrent: f32;
|
||||
}
|
||||
}
|
||||
|
||||
use crate::core_media::{
|
||||
dispatch_queue_create, AVMediaTypeVideo, CMSampleBufferGetImageBuffer,
|
||||
CMVideoFormatDescriptionGetDimensions, CVImageBufferRef, CVPixelBufferGetBaseAddress,
|
||||
CVPixelBufferGetDataSize, CVPixelBufferLockBaseAddress, CVPixelBufferUnlockBaseAddress,
|
||||
NSObject,
|
||||
dispatch_queue_create, AVCaptureExposureDurationCurrent, AVCaptureExposureTargetBiasCurrent,
|
||||
AVCaptureISOCurrent, AVCaptureWhiteBalanceGains, AVMediaTypeVideo, CGPoint,
|
||||
CMSampleBufferGetImageBuffer, CMVideoFormatDescriptionGetDimensions, CVImageBufferRef,
|
||||
CVPixelBufferGetBaseAddress, CVPixelBufferGetDataSize, CVPixelBufferLockBaseAddress,
|
||||
CVPixelBufferUnlockBaseAddress, NSObject,
|
||||
};
|
||||
use crate::core_media::{
|
||||
AVMediaTypeAudio, AVMediaTypeClosedCaption, AVMediaTypeDepthData, AVMediaTypeMetadata,
|
||||
@@ -185,14 +209,18 @@ use cocoa_foundation::foundation::{NSArray, NSInteger, NSString, NSUInteger};
|
||||
use core_media_sys::{
|
||||
kCMPixelFormat_422YpCbCr8_yuvs, kCMPixelFormat_8IndexedGray_WhiteIsZero,
|
||||
kCMVideoCodecType_422YpCbCr8, kCMVideoCodecType_JPEG, kCMVideoCodecType_JPEG_OpenDML,
|
||||
CMFormatDescriptionGetMediaSubType, CMFormatDescriptionRef, CMSampleBufferRef,
|
||||
CMFormatDescriptionGetMediaSubType, CMFormatDescriptionRef, CMSampleBufferRef, CMTime,
|
||||
CMVideoDimensions,
|
||||
};
|
||||
use flume::{Receiver, Sender};
|
||||
use nokhwa_core::types::{
|
||||
CameraControl, ControlValueDescription, KnownCameraControl, KnownCameraControlFlag,
|
||||
};
|
||||
use nokhwa_core::{
|
||||
error::NokhwaError,
|
||||
types::{ApiBackend, CameraFormat, CameraIndex, CameraInfo, FrameFormat, Resolution},
|
||||
};
|
||||
use objc::runtime::NO;
|
||||
use objc::{
|
||||
declare::ClassDecl,
|
||||
runtime::{Class, Object, Protocol, Sel, BOOL, YES},
|
||||
@@ -204,11 +232,12 @@ use std::{
|
||||
collections::HashSet,
|
||||
convert::TryFrom,
|
||||
error::Error,
|
||||
ffi::{c_void, CStr},
|
||||
ffi::{c_float, c_void, CStr},
|
||||
sync::{Arc, Mutex},
|
||||
};
|
||||
|
||||
const UTF8_ENCODING: usize = 4;
|
||||
type CGFloat = c_float;
|
||||
|
||||
macro_rules! create_boilerplate_impl {
|
||||
{
|
||||
@@ -336,8 +365,6 @@ static CALLBACK_CLASS: Lazy<&'static Class> = Lazy::new(|| {
|
||||
// frame stack
|
||||
// oooh scary provenannce-breaking BULLSHIT AAAAAA I LOVE TYPE ERASURE
|
||||
decl.add_ivar::<*const c_void>("_arcmutptr"); // ArkMutex, the not-arknights totally not gacha totally not ripoff new vidya game from l-pleasestop-npengtul
|
||||
// KILL ME KILL ME KILL ME PLEASE KILL ME I DONT WANT TO LIVE ANYMORE
|
||||
// i draw myself getting hurt and murdered in various ways to distract from my urges self harm
|
||||
|
||||
extern "C" fn my_callback_get_arcmutptr(this: &Object, _: Sel) -> *const c_void {
|
||||
unsafe { *this.get_ivar("_arcmutptr") }
|
||||
@@ -1036,6 +1063,456 @@ impl AVCaptureDevice {
|
||||
self.unlock();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// 0 => Focus POI
|
||||
// 1 => Focus Manual Setting
|
||||
// 2 => Exposure POI
|
||||
// 3 => Exposure Face Driven
|
||||
// 4 => Exposure Target Bias
|
||||
// 5 => Exposure ISO
|
||||
// 6 => Exposure Duration
|
||||
pub fn get_controls(&mut self) -> Result<Vec<CameraControl>, NokhwaError> {
|
||||
let active_format: *mut Object = unsafe { msg_send![self.inner, activeFormat] };
|
||||
|
||||
let mut controls = vec![];
|
||||
// get focus modes
|
||||
|
||||
let focus_current: NSInteger = unsafe { msg_send![self.inner, focusMode] };
|
||||
let focus_locked: BOOL =
|
||||
unsafe { msg_send![self.inner, isFocusModeSupported:NSInteger::from(0)] };
|
||||
let focus_auto: BOOL =
|
||||
unsafe { msg_send![self.inner, isFocusModeSupported:NSInteger::from(1)] };
|
||||
let focus_continuous: BOOL =
|
||||
unsafe { msg_send![self.inner, isFocusModeSupported:NSInteger::from(2)] };
|
||||
|
||||
{
|
||||
let mut supported_focus_values = vec![];
|
||||
|
||||
if focus_locked == YES {
|
||||
supported_focus_values.push(0)
|
||||
}
|
||||
if focus_auto == YES {
|
||||
supported_focus_values.push(1)
|
||||
}
|
||||
if focus_continuous == YES {
|
||||
supported_focus_values.push(2)
|
||||
}
|
||||
|
||||
controls.push(CameraControl::new(
|
||||
KnownCameraControl::Focus,
|
||||
"FocusMode".to_string(),
|
||||
ControlValueDescription::Enum {
|
||||
value: focus_current,
|
||||
possible: supported_focus_values,
|
||||
default: focus_current,
|
||||
},
|
||||
vec![],
|
||||
true,
|
||||
));
|
||||
}
|
||||
|
||||
let focus_poi_supported: BOOL =
|
||||
unsafe { msg_send![self.inner, isFocusPointOfInterestSupported] };
|
||||
let focus_poi: CGPoint = unsafe { msg_send![self.inner, focusPointOfInterest] };
|
||||
|
||||
controls.push(CameraControl::new(
|
||||
KnownCameraControl::Other(0),
|
||||
"FocusPointOfInterest".to_string(),
|
||||
ControlValueDescription::Point {
|
||||
value: (focus_poi.x as f64, focus_poi.y as f64),
|
||||
default: (0.5, 0.5),
|
||||
},
|
||||
if focus_poi_supported == NO {
|
||||
vec![KnownCameraControlFlag::Disabled]
|
||||
} else {
|
||||
vec![]
|
||||
},
|
||||
focus_auto == YES || focus_continuous == YES,
|
||||
));
|
||||
|
||||
let focus_manual: BOOL =
|
||||
unsafe { msg_send![self.inner, isLockingFocusWithCustomLensPositionSupported] };
|
||||
let focus_lenspos: f32 = unsafe { msg_send![self.inner, lensPosition] };
|
||||
|
||||
controls.push(CameraControl::new(
|
||||
KnownCameraControl::Other(1),
|
||||
"FocusManualLensPosition".to_string(),
|
||||
ControlValueDescription::FloatRange {
|
||||
min: 0.0,
|
||||
max: 1.0,
|
||||
value: focus_lenspos as f64,
|
||||
step: f64::MIN_POSITIVE,
|
||||
default: 1.0,
|
||||
},
|
||||
if focus_manual == YES {
|
||||
vec![]
|
||||
} else {
|
||||
vec![KnownCameraControlFlag::Disabled]
|
||||
},
|
||||
focus_manual == YES,
|
||||
));
|
||||
|
||||
// get exposures
|
||||
let exposure_current: NSInteger = unsafe { msg_send![self.inner, exposureMode] };
|
||||
let exposure_locked: BOOL =
|
||||
unsafe { msg_send![self.inner, isExposureModeSupported:NSInteger::from(0)] };
|
||||
let exposure_auto: BOOL =
|
||||
unsafe { msg_send![self.inner, isExposureModeSupported:NSInteger::from(1)] };
|
||||
let exposure_continuous: BOOL =
|
||||
unsafe { msg_send![self.inner, isExposureModeSupported:NSInteger::from(2)] };
|
||||
let exposure_custom: BOOL =
|
||||
unsafe { msg_send![self.inner, isExposureModeSupported:NSInteger::from(3)] };
|
||||
|
||||
{
|
||||
let mut supported_exposure_values = vec![];
|
||||
|
||||
if exposure_locked == YES {
|
||||
supported_exposure_values.push(0);
|
||||
}
|
||||
if exposure_auto == YES {
|
||||
supported_exposure_values.push(1);
|
||||
}
|
||||
if exposure_continuous == YES {
|
||||
supported_exposure_values.push(2);
|
||||
}
|
||||
if exposure_custom == YES {
|
||||
supported_exposure_values.push(3);
|
||||
}
|
||||
|
||||
controls.push(CameraControl::new(
|
||||
KnownCameraControl::Exposure,
|
||||
"ExposureMode".to_string(),
|
||||
ControlValueDescription::Enum {
|
||||
value: exposure_current,
|
||||
possible: supported_exposure_values,
|
||||
default: exposure_current,
|
||||
},
|
||||
vec![],
|
||||
true,
|
||||
));
|
||||
}
|
||||
|
||||
let exposure_poi_supported: BOOL =
|
||||
unsafe { msg_send![self.inner, isExposurePointOfInterestSupported] };
|
||||
let exposure_poi: CGPoint = unsafe { msg_send![self.inner, exposurePointOfInterest] };
|
||||
|
||||
controls.push(CameraControl::new(
|
||||
KnownCameraControl::Other(2),
|
||||
"ExposurePointOfInterest".to_string(),
|
||||
ControlValueDescription::Point {
|
||||
value: (exposure_poi.x as f64, exposure_poi.y as f64),
|
||||
default: (0.5, 0.5),
|
||||
},
|
||||
if exposure_poi_supported == NO {
|
||||
vec![KnownCameraControlFlag::Disabled]
|
||||
} else {
|
||||
vec![]
|
||||
},
|
||||
focus_auto == YES || focus_continuous == YES,
|
||||
));
|
||||
|
||||
let expposure_face_driven_supported: BOOL =
|
||||
unsafe { msg_send![self.inner, isFaceDrivenAutoExposureEnabled] };
|
||||
let exposure_face_driven: BOOL = unsafe {
|
||||
msg_send![
|
||||
self.inner,
|
||||
automaticallyAdjustsFaceDrivenAutoExposureEnabled
|
||||
]
|
||||
};
|
||||
|
||||
controls.push(CameraControl::new(
|
||||
KnownCameraControl::Other(3),
|
||||
"ExposureFaceDriven".to_string(),
|
||||
ControlValueDescription::Boolean {
|
||||
value: exposure_face_driven == YES,
|
||||
default: false,
|
||||
},
|
||||
if expposure_face_driven_supported == NO {
|
||||
vec![KnownCameraControlFlag::Disabled]
|
||||
} else {
|
||||
vec![]
|
||||
},
|
||||
exposure_poi_supported == YES,
|
||||
));
|
||||
|
||||
let exposure_bias: f32 = unsafe { msg_send![self.inner, exposureTargetBias] };
|
||||
let exposure_bias_min: f32 = unsafe { msg_send![self.inner, minExposureTargetBias] };
|
||||
let exposure_bias_max: f32 = unsafe { msg_send![self.inner, maxExposureTargetBias] };
|
||||
|
||||
controls.push(CameraControl::new(
|
||||
KnownCameraControl::Other(4),
|
||||
"ExposureBiasTarget".to_string(),
|
||||
ControlValueDescription::FloatRange {
|
||||
min: exposure_bias_min as f64,
|
||||
max: exposure_bias_max as f64,
|
||||
value: exposure_bias as f64,
|
||||
step: f32::MIN_POSITIVE as f64,
|
||||
default: unsafe { AVCaptureExposureTargetBiasCurrent } as f64,
|
||||
},
|
||||
vec![],
|
||||
true,
|
||||
));
|
||||
|
||||
let exposure_duration: CMTime = unsafe { msg_send![self.inner, exposureDuration] };
|
||||
let exposure_duration_min: CMTime =
|
||||
unsafe { msg_send![active_format, minExposureDuration] };
|
||||
let exposure_duration_max: CMTime =
|
||||
unsafe { msg_send![active_format, maxExposureDuration] };
|
||||
|
||||
controls.push(CameraControl::new(
|
||||
KnownCameraControl::Other(5),
|
||||
"ExposureDuration".to_string(),
|
||||
ControlValueDescription::IntegerRange {
|
||||
min: exposure_duration_min.value,
|
||||
max: exposure_duration_max.value,
|
||||
value: exposure_duration.value,
|
||||
step: 1,
|
||||
default: unsafe { AVCaptureExposureDurationCurrent.value },
|
||||
},
|
||||
if exposure_custom == YES {
|
||||
vec![
|
||||
KnownCameraControlFlag::ReadOnly,
|
||||
KnownCameraControlFlag::Volatile,
|
||||
]
|
||||
} else {
|
||||
vec![KnownCameraControlFlag::Volatile]
|
||||
},
|
||||
exposure_custom == YES,
|
||||
));
|
||||
|
||||
let exposure_iso: f32 = unsafe { msg_send![self.inner, ISO] };
|
||||
let exposure_iso_min: f32 = unsafe { msg_send![active_format, minISO] };
|
||||
let exposure_iso_max: f32 = unsafe { msg_send![active_format, maxISO] };
|
||||
|
||||
controls.push(CameraControl::new(
|
||||
KnownCameraControl::Other(6),
|
||||
"ExposureISO".to_string(),
|
||||
ControlValueDescription::FloatRange {
|
||||
min: exposure_iso_min as f64,
|
||||
max: exposure_iso_max as f64,
|
||||
value: exposure_iso as f64,
|
||||
step: f32::MIN_POSITIVE as f64,
|
||||
default: unsafe { AVCaptureISOCurrent } as f64,
|
||||
},
|
||||
if exposure_custom == YES {
|
||||
vec![
|
||||
KnownCameraControlFlag::ReadOnly,
|
||||
KnownCameraControlFlag::Volatile,
|
||||
]
|
||||
} else {
|
||||
vec![KnownCameraControlFlag::Volatile]
|
||||
},
|
||||
exposure_custom == YES,
|
||||
));
|
||||
|
||||
let lens_aperture: f32 = unsafe { msg_send![self.inner, lensAperture] };
|
||||
|
||||
controls.push(CameraControl::new(
|
||||
KnownCameraControl::Other(7),
|
||||
"LensAperture".to_string(),
|
||||
ControlValueDescription::Float {
|
||||
value: lens_aperture as f64,
|
||||
default: lens_aperture as f64,
|
||||
step: lens_aperture as f64,
|
||||
},
|
||||
vec![KnownCameraControlFlag::ReadOnly],
|
||||
false,
|
||||
));
|
||||
|
||||
// get whiteblaance
|
||||
let white_balance_current: NSInteger = unsafe { msg_send![self.inner, whiteBalanceMode] };
|
||||
let white_balance_manual: BOOL =
|
||||
unsafe { msg_send![self.inner, isWhiteBalanceModeSupported:NSInteger::from(0)] };
|
||||
let white_balance_auto: BOOL =
|
||||
unsafe { msg_send![self.inner, isWhiteBalanceModeSupported:NSInteger::from(1)] };
|
||||
let white_balance_continuous: BOOL =
|
||||
unsafe { msg_send![self.inner, isWhiteBalanceModeSupported:NSInteger::from(2)] };
|
||||
|
||||
{
|
||||
let mut possible = vec![];
|
||||
|
||||
if white_balance_manual == YES {
|
||||
possible.push(0);
|
||||
}
|
||||
if white_balance_auto == YES {
|
||||
possible.push(1);
|
||||
}
|
||||
if white_balance_continuous == YES {
|
||||
possible.push(2);
|
||||
}
|
||||
|
||||
controls.push(CameraControl::new(
|
||||
KnownCameraControl::WhiteBalance,
|
||||
"WhiteBalanceMode".to_string(),
|
||||
ControlValueDescription::Enum {
|
||||
value: white_balance_current as i64,
|
||||
possible,
|
||||
default: 0,
|
||||
},
|
||||
vec![],
|
||||
true,
|
||||
));
|
||||
}
|
||||
|
||||
let white_balance_gains: AVCaptureWhiteBalanceGains =
|
||||
unsafe { msg_send![self.inner, deviceWhiteBalanceGains] };
|
||||
let white_balance_default: AVCaptureWhiteBalanceGains =
|
||||
unsafe { msg_send![self.inner, grayWorldDeviceWhiteBalanceGains] };
|
||||
let white_balancne_max: AVCaptureWhiteBalanceGains =
|
||||
unsafe { msg_send![self.inner, maxWhiteBalanceGain] };
|
||||
let white_balance_gain_supported: BOOL = unsafe {
|
||||
msg_send![
|
||||
self.inner,
|
||||
isLockingWhiteBalanceWithCustomDeviceGainsSupported
|
||||
]
|
||||
};
|
||||
|
||||
controls.push(CameraControl::new(
|
||||
KnownCameraControl::Gain,
|
||||
"WhiteBalanceGain".to_string(),
|
||||
ControlValueDescription::RGB {
|
||||
value: (
|
||||
white_balance_gains.redGain as f64,
|
||||
white_balance_gains.greenGain as f64,
|
||||
white_balance_gains.blueGain as f64,
|
||||
),
|
||||
max: (
|
||||
white_balancne_max.redGain as f64,
|
||||
white_balancne_max.greenGain as f64,
|
||||
white_balancne_max.blueGain as f64,
|
||||
),
|
||||
default: (
|
||||
white_balance_default.redGain as f64,
|
||||
white_balance_default.greenGain as f64,
|
||||
white_balance_default.blueGain as f64,
|
||||
),
|
||||
},
|
||||
if white_balance_gain_supported == YES {
|
||||
vec![
|
||||
KnownCameraControlFlag::Disabled,
|
||||
KnownCameraControlFlag::ReadOnly,
|
||||
]
|
||||
} else {
|
||||
vec![]
|
||||
},
|
||||
white_balance_gain_supported == YES,
|
||||
));
|
||||
|
||||
// get flash
|
||||
let has_torch: BOOL = unsafe { msg_send![self.inner, torchAvailible] };
|
||||
let torch_active: BOOL = unsafe { msg_send![self.inner, isTorchActive] };
|
||||
let torch_off: BOOL =
|
||||
unsafe { msg_send![self.inner, isTorchModeSupported:NSInteger::from(0)] };
|
||||
let torch_on: BOOL =
|
||||
unsafe { msg_send![self.inner, isTorchModeSupported:NSInteger::from(1)] };
|
||||
let torch_auto: BOOL =
|
||||
unsafe { msg_send![self.inner, isTorchModeSupported:NSInteger::from(2)] };
|
||||
|
||||
{
|
||||
let mut possible = vec![];
|
||||
|
||||
if torch_off == YES {
|
||||
possible.push(0);
|
||||
}
|
||||
if torch_on == YES {
|
||||
possible.push(1);
|
||||
}
|
||||
if torch_auto == YES {
|
||||
possible.push(2);
|
||||
}
|
||||
|
||||
controls.push(CameraControl::new(
|
||||
KnownCameraControl::Other(8),
|
||||
"Flash".to_string(),
|
||||
ControlValueDescription::Enum {
|
||||
value: (torch_active == YES) as i64,
|
||||
possible,
|
||||
default: 0,
|
||||
},
|
||||
if has_torch == YES {
|
||||
vec![KnownCameraControlFlag::Disabled]
|
||||
} else {
|
||||
vec![]
|
||||
},
|
||||
has_torch == YES,
|
||||
));
|
||||
}
|
||||
|
||||
// get low light boost
|
||||
let has_llb: BOOL = unsafe { msg_send![self.inner, lowLightBoostSupported] };
|
||||
let llb_enabled: BOOL = unsafe { msg_send![self.inner, lowLightBoostEnabled] };
|
||||
|
||||
{
|
||||
let mut possible = vec![];
|
||||
|
||||
if has_llb == YES {
|
||||
possible.push(0); // off
|
||||
possible.push(1); // on
|
||||
possible.push(2); // auto
|
||||
}
|
||||
|
||||
controls.push(CameraControl::new(
|
||||
KnownCameraControl::BacklightComp,
|
||||
"LowLightCompensation".to_string(),
|
||||
ControlValueDescription::Enum {
|
||||
value: (llb_enabled == YES) as i64,
|
||||
possible,
|
||||
default: 0,
|
||||
},
|
||||
if has_llb == YES {
|
||||
vec![KnownCameraControlFlag::Disabled]
|
||||
} else {
|
||||
vec![]
|
||||
},
|
||||
has_llb == YES,
|
||||
));
|
||||
}
|
||||
|
||||
// get zoom factor
|
||||
let zoom_current: CGFloat = unsafe { msg_send![self.inner, videoZoomFactor] };
|
||||
let zoom_min: CGFloat = unsafe { msg_send![self.inner, minAvailableVideoZoomFactor] };
|
||||
let zoom_max: CGFloat = unsafe { msg_send![self.inner, maxAvailableVideoZoomFactor] };
|
||||
|
||||
controls.push(CameraControl::new(
|
||||
KnownCameraControl::Zoom,
|
||||
"Zoom".to_string(),
|
||||
ControlValueDescription::FloatRange {
|
||||
min: zoom_min as f64,
|
||||
max: zoom_max as f64,
|
||||
value: zoom_current as f64,
|
||||
step: f32::MIN_POSITIVE as f64,
|
||||
default: 1.0,
|
||||
},
|
||||
vec![],
|
||||
true,
|
||||
));
|
||||
|
||||
// zoom distortion correction
|
||||
let distortion_correction_supported: BOOL =
|
||||
unsafe { msg_send![self.inner, geometricDistortionCorrectionSupported] };
|
||||
let distortion_correction_current_value: BOOL =
|
||||
unsafe { msg_send![self.inner, geometricDistortionCorrectionEnabled] };
|
||||
|
||||
controls.push(CameraControl::new(
|
||||
KnownCameraControl::Other(9),
|
||||
"DistortionCorrection".to_string(),
|
||||
ControlValueDescription::Boolean {
|
||||
value: distortion_correction_current_value == YES,
|
||||
default: false,
|
||||
},
|
||||
if distortion_correction_supported == YES {
|
||||
vec![
|
||||
KnownCameraControlFlag::ReadOnly,
|
||||
KnownCameraControlFlag::Disabled,
|
||||
]
|
||||
} else {
|
||||
vec![]
|
||||
},
|
||||
distortion_correction_supported == YES,
|
||||
));
|
||||
|
||||
Ok(controls)
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for AVCaptureDevice {
|
||||
|
||||
@@ -835,8 +835,6 @@ pub mod wmf {
|
||||
control,
|
||||
control.to_string(),
|
||||
ctrl_value_set,
|
||||
vec![is_manual],
|
||||
true,
|
||||
))
|
||||
}
|
||||
|
||||
|
||||
@@ -18,10 +18,12 @@ use crate::types::{
|
||||
buf_mjpeg_to_rgb, buf_yuyv422_to_rgb, mjpeg_to_rgb, yuyv422_to_rgb, FrameFormat,
|
||||
};
|
||||
use image::{Luma, LumaA, Pixel, Rgb, Rgba};
|
||||
use std::fmt::Debug;
|
||||
|
||||
/// Trait that has methods to convert raw data from the webcam to a proper raw image.
|
||||
pub trait FormatDecoder {
|
||||
pub trait FormatDecoder: Copy + Clone + Debug + Default + Sized + Send + Sync + {
|
||||
type Output: Pixel<Subpixel = u8>;
|
||||
const FORMATS: &'static [FrameFormat];
|
||||
|
||||
/// Allocates and returns a `Vec`
|
||||
/// # Errors
|
||||
@@ -49,7 +51,8 @@ pub struct RgbFormat;
|
||||
|
||||
impl FormatDecoder for RgbFormat {
|
||||
type Output = Rgb<u8>;
|
||||
|
||||
const FORMATS: &'static [FrameFormat] = &[FrameFormat::MJPEG, FrameFormat::YUYV];
|
||||
|
||||
fn write_output(fcc: FrameFormat, data: &[u8]) -> Result<Vec<u8>, NokhwaError> {
|
||||
match fcc {
|
||||
FrameFormat::MJPEG => mjpeg_to_rgb(data, false),
|
||||
@@ -105,6 +108,8 @@ pub struct RgbAFormat;
|
||||
impl FormatDecoder for RgbAFormat {
|
||||
type Output = Rgba<u8>;
|
||||
|
||||
const FORMATS: &'static [FrameFormat] = &[FrameFormat::MJPEG, FrameFormat::YUYV];
|
||||
|
||||
fn write_output(fcc: FrameFormat, data: &[u8]) -> Result<Vec<u8>, NokhwaError> {
|
||||
match fcc {
|
||||
FrameFormat::MJPEG => mjpeg_to_rgb(data, true),
|
||||
@@ -161,6 +166,10 @@ pub struct LumaFormat;
|
||||
impl FormatDecoder for LumaFormat {
|
||||
type Output = Luma<u8>;
|
||||
|
||||
const FORMATS: &'static [FrameFormat] =
|
||||
&[FrameFormat::MJPEG, FrameFormat::YUYV, FrameFormat::GRAY];
|
||||
|
||||
|
||||
#[allow(clippy::cast_possible_truncation)]
|
||||
fn write_output(fcc: FrameFormat, data: &[u8]) -> Result<Vec<u8>, NokhwaError> {
|
||||
match fcc {
|
||||
@@ -227,6 +236,9 @@ pub struct LumaAFormat;
|
||||
impl FormatDecoder for LumaAFormat {
|
||||
type Output = LumaA<u8>;
|
||||
|
||||
const FORMATS: &'static [FrameFormat] =
|
||||
&[FrameFormat::MJPEG, FrameFormat::YUYV, FrameFormat::GRAY];
|
||||
|
||||
#[allow(clippy::cast_possible_truncation)]
|
||||
fn write_output(fcc: FrameFormat, data: &[u8]) -> Result<Vec<u8>, NokhwaError> {
|
||||
match fcc {
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
use crate::types::KnownCameraControlFlag;
|
||||
use crate::{
|
||||
buffer::Buffer,
|
||||
error::NokhwaError,
|
||||
|
||||
+167
-18
@@ -1,4 +1,5 @@
|
||||
use crate::error::NokhwaError;
|
||||
use crate::pixel_format::FormatDecoder;
|
||||
#[cfg(feature = "serialize")]
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{
|
||||
@@ -15,7 +16,7 @@ use std::{
|
||||
/// - `None`: Pick a random [`CameraFormat`]
|
||||
#[derive(Copy, Clone, Debug, Hash, Ord, PartialOrd, Eq, PartialEq)]
|
||||
#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
|
||||
pub enum RequestedFormat {
|
||||
pub enum RequestedFormatType {
|
||||
HighestResolution,
|
||||
HighestFrameRate,
|
||||
Exact(CameraFormat),
|
||||
@@ -23,37 +24,81 @@ pub enum RequestedFormat {
|
||||
None,
|
||||
}
|
||||
|
||||
impl RequestedFormat {
|
||||
impl Default for RequestedFormatType {
|
||||
fn default() -> Self {
|
||||
RequestedFormatType::None
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for RequestedFormatType {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{:?}", self)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Hash, Ord, PartialOrd, Eq, PartialEq)]
|
||||
#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
|
||||
pub struct RequestedFormat<F>
|
||||
where
|
||||
F: FormatDecoder,
|
||||
{
|
||||
requested_format: RequestedFormatType,
|
||||
wanted_decoder: F,
|
||||
}
|
||||
|
||||
impl<F> RequestedFormat<F>
|
||||
where
|
||||
F: FormatDecoder,
|
||||
{
|
||||
pub fn new<Decoder: FormatDecoder>(requested: RequestedFormatType) -> RequestedFormat<Decoder> {
|
||||
RequestedFormat {
|
||||
requested_format: requested,
|
||||
wanted_decoder: Decoder::default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Fulfill the requested using a list of all available formats.
|
||||
pub fn fulfill(&self, all_formats: &[CameraFormat]) -> Option<CameraFormat> {
|
||||
match self {
|
||||
RequestedFormat::HighestResolution => {
|
||||
match self.requested_format {
|
||||
RequestedFormatType::HighestResolution => {
|
||||
let mut formats = all_formats.to_vec();
|
||||
formats.sort_by_key(|a| a.resolution());
|
||||
let resolution = *formats.iter().last()?;
|
||||
let mut format_resolutions = formats
|
||||
.into_iter()
|
||||
.filter(|fmt| fmt.resolution() == resolution.resolution())
|
||||
.filter(|fmt| {
|
||||
fmt.resolution() == resolution.resolution()
|
||||
&& F::FORMATS.contains(&fmt.format())
|
||||
})
|
||||
.collect::<Vec<CameraFormat>>();
|
||||
format_resolutions.sort_by_key(|a| a.frame_rate());
|
||||
format_resolutions.last().copied()
|
||||
}
|
||||
RequestedFormat::HighestFrameRate => {
|
||||
RequestedFormatType::HighestFrameRate => {
|
||||
let mut formats = all_formats.to_vec();
|
||||
formats.sort_by_key(|a| a.frame_rate());
|
||||
let frame_rate = *formats.iter().last()?;
|
||||
let mut format_framerates = formats
|
||||
.into_iter()
|
||||
.filter(|fmt| fmt.frame_rate() == frame_rate.frame_rate())
|
||||
.filter(|fmt| {
|
||||
fmt.frame_rate() == frame_rate.frame_rate()
|
||||
&& F::FORMATS.contains(&fmt.format())
|
||||
})
|
||||
.collect::<Vec<CameraFormat>>();
|
||||
format_framerates.sort_by_key(|a| a.resolution());
|
||||
format_framerates.last().copied()
|
||||
}
|
||||
RequestedFormat::Exact(fmt) => Some(*fmt),
|
||||
RequestedFormat::Closest(c) => {
|
||||
RequestedFormatType::Exact(fmt) => {
|
||||
if F::FORMATS.contains(&fmt.format()) {
|
||||
Some(fmt)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
RequestedFormatType::Closest(c) => {
|
||||
let same_fmt_formats = all_formats
|
||||
.iter()
|
||||
.filter(|x| x.format() == c.format())
|
||||
.filter(|x| x.format() == c.format() && F::FORMATS.contains(&x.format()))
|
||||
.copied()
|
||||
.collect::<Vec<CameraFormat>>();
|
||||
let mut resolution_map = same_fmt_formats
|
||||
@@ -91,12 +136,19 @@ impl RequestedFormat {
|
||||
let frame_rate = framerate_map.first()?.1;
|
||||
Some(CameraFormat::new(resolution, c.format(), frame_rate))
|
||||
}
|
||||
RequestedFormat::None => all_formats.first().copied(),
|
||||
RequestedFormatType::None => all_formats
|
||||
.iter()
|
||||
.filter(|fmt| F::FORMATS.contains(&fmt.format()))
|
||||
.nth(0)
|
||||
.copied(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for RequestedFormat {
|
||||
impl<F> Display for RequestedFormat<F>
|
||||
where
|
||||
F: FormatDecoder,
|
||||
{
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{:?}", self)
|
||||
}
|
||||
@@ -594,11 +646,11 @@ impl Display for KnownCameraControl {
|
||||
pub enum KnownCameraControlFlag {
|
||||
Automatic,
|
||||
Manual,
|
||||
Continuous,
|
||||
ReadOnly,
|
||||
WriteOnly,
|
||||
Volatile,
|
||||
Disabled,
|
||||
Inactive,
|
||||
}
|
||||
|
||||
impl Display for KnownCameraControlFlag {
|
||||
@@ -651,6 +703,25 @@ pub enum ControlValueDescription {
|
||||
value: Vec<u8>,
|
||||
default: Vec<u8>,
|
||||
},
|
||||
KeyValuePair {
|
||||
key: i128,
|
||||
value: i128,
|
||||
default: (i128, i128),
|
||||
},
|
||||
Point {
|
||||
value: (f64, f64),
|
||||
default: (f64, f64),
|
||||
},
|
||||
Enum {
|
||||
value: i64,
|
||||
possible: Vec<i64>,
|
||||
default: i64,
|
||||
},
|
||||
RGB {
|
||||
value: (f64, f64, f64),
|
||||
max: (f64, f64, f64),
|
||||
default: (f64, f64, f64),
|
||||
},
|
||||
}
|
||||
|
||||
impl ControlValueDescription {
|
||||
@@ -674,6 +745,16 @@ impl ControlValueDescription {
|
||||
ControlValueDescription::Bytes { value, .. } => {
|
||||
ControlValueSetter::Bytes(value.clone())
|
||||
}
|
||||
ControlValueDescription::KeyValuePair { key, value, .. } => {
|
||||
ControlValueSetter::KeyValue(*key, *value)
|
||||
}
|
||||
ControlValueDescription::Point { value, .. } => {
|
||||
ControlValueSetter::Point(value.0, value.1)
|
||||
}
|
||||
ControlValueDescription::Enum { value, .. } => ControlValueSetter::EnumValue(*value),
|
||||
ControlValueDescription::RGB { value, .. } => {
|
||||
ControlValueSetter::RGB(value.0, value.1, value.2)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -737,6 +818,18 @@ impl ControlValueDescription {
|
||||
ControlValueSetter::Bytes(_) => {
|
||||
matches!(self, ControlValueDescription::Bytes { .. })
|
||||
}
|
||||
ControlValueSetter::KeyValue(_, _) => {
|
||||
matches!(self, ControlValueDescription::KeyValuePair { .. })
|
||||
}
|
||||
ControlValueSetter::Point(_, _) => {
|
||||
matches!(self, ControlValueDescription::Point { .. })
|
||||
}
|
||||
ControlValueSetter::EnumValue(_) => {
|
||||
matches!(self, ControlValueDescription::Enum { .. })
|
||||
}
|
||||
ControlValueSetter::RGB(_, _, _) => {
|
||||
matches!(self, ControlValueDescription::RGB { .. })
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -804,6 +897,46 @@ impl Display for ControlValueDescription {
|
||||
ControlValueDescription::Bytes { value, default } => {
|
||||
write!(f, "(Current: {:x?}, Default: {:x?})", value, default)
|
||||
}
|
||||
ControlValueDescription::KeyValuePair {
|
||||
key,
|
||||
value,
|
||||
default,
|
||||
} => {
|
||||
write!(
|
||||
f,
|
||||
"Current: ({}, {}), Default: ({}, {})",
|
||||
key, value, default.0, default.1
|
||||
)
|
||||
}
|
||||
ControlValueDescription::Point { value, default } => {
|
||||
write!(
|
||||
f,
|
||||
"Current: ({}, {}), Default: ({}, {})",
|
||||
value.0, value.1, default.0, default.1
|
||||
)
|
||||
}
|
||||
ControlValueDescription::Enum {
|
||||
value,
|
||||
possible,
|
||||
default,
|
||||
} => {
|
||||
write!(
|
||||
f,
|
||||
"Current: {}, Possible Values: {:?}, Default: {}",
|
||||
value, possible, default
|
||||
)
|
||||
}
|
||||
ControlValueDescription::RGB {
|
||||
value,
|
||||
max,
|
||||
default,
|
||||
} => {
|
||||
write!(
|
||||
f,
|
||||
"Current: ({}, {}, {}), Max: ({}, {}, {}), Default: ({}, {}, {})",
|
||||
value.0, value.1, value.2, max.0, max.1, max.2, default.0, default.1, default.2
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -918,6 +1051,10 @@ pub enum ControlValueSetter {
|
||||
Boolean(bool),
|
||||
String(String),
|
||||
Bytes(Vec<u8>),
|
||||
KeyValue(i128, i128),
|
||||
Point(f64, f64),
|
||||
EnumValue(i64),
|
||||
RGB(f64, f64, f64),
|
||||
}
|
||||
|
||||
impl Display for ControlValueSetter {
|
||||
@@ -927,19 +1064,31 @@ impl Display for ControlValueSetter {
|
||||
write!(f, "Value: None")
|
||||
}
|
||||
ControlValueSetter::Integer(i) => {
|
||||
write!(f, "Value: {}", i)
|
||||
write!(f, "IntegerValue: {}", i)
|
||||
}
|
||||
ControlValueSetter::Float(d) => {
|
||||
write!(f, "Value: {}", d)
|
||||
write!(f, "FloatValue: {}", d)
|
||||
}
|
||||
ControlValueSetter::Boolean(b) => {
|
||||
write!(f, "Value: {}", b)
|
||||
write!(f, "BoolValue: {}", b)
|
||||
}
|
||||
ControlValueSetter::String(s) => {
|
||||
write!(f, "Value: {}", s)
|
||||
write!(f, "StrValue: {}", s)
|
||||
}
|
||||
ControlValueSetter::Bytes(b) => {
|
||||
write!(f, "Value: {:x?}", b)
|
||||
write!(f, "BytesValue: {:x?}", b)
|
||||
}
|
||||
ControlValueSetter::KeyValue(k, v) => {
|
||||
write!(f, "KVValue: ({}, {})", k, v)
|
||||
}
|
||||
ControlValueSetter::Point(x, y) => {
|
||||
write!(f, "PointValue: ({}, {})", x, y)
|
||||
}
|
||||
ControlValueSetter::EnumValue(v) => {
|
||||
write!(f, "EnumValue: {}", v)
|
||||
}
|
||||
ControlValueSetter::RGB(r, g, b) => {
|
||||
write!(f, "RGBValue: ({}, {}, {})", r, g, b)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -200,26 +200,20 @@ impl CaptureBackendTrait for AVFoundationCaptureDevice {
|
||||
self.set_camera_format(format)
|
||||
}
|
||||
|
||||
fn camera_control(&self, _: KnownCameraControl) -> Result<CameraControl, NokhwaError> {
|
||||
Err(NokhwaError::NotImplementedError(
|
||||
"Not Implemented".to_string(),
|
||||
))
|
||||
fn camera_control(&self, control: KnownCameraControl) -> Result<CameraControl, NokhwaError> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn camera_controls(&self) -> Result<Vec<CameraControl>, NokhwaError> {
|
||||
Err(NokhwaError::NotImplementedError(
|
||||
"Not Implemented".to_string(),
|
||||
))
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn set_camera_control(
|
||||
&mut self,
|
||||
_: KnownCameraControl,
|
||||
_: ControlValueSetter,
|
||||
id: KnownCameraControl,
|
||||
value: ControlValueSetter,
|
||||
) -> Result<(), NokhwaError> {
|
||||
Err(NokhwaError::NotImplementedError(
|
||||
"Not Implemented".to_string(),
|
||||
))
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn open_stream(&mut self) -> Result<(), NokhwaError> {
|
||||
|
||||
Reference in New Issue
Block a user