mirror of
https://github.com/l1npengtul/nokhwa.git
synced 2026-07-04 02:27:26 +00:00
fix windows media foundation
This commit is contained in:
@@ -0,0 +1,19 @@
|
|||||||
|
<component name="ProjectRunConfigurationManager">
|
||||||
|
<configuration default="false" name="Clippy Main Windows" type="CargoCommandRunConfiguration" factoryName="Cargo Command">
|
||||||
|
<option name="command" value="clippy --features "output-wgpu, input-msmf"" />
|
||||||
|
<option name="workingDirectory" value="file://$PROJECT_DIR$" />
|
||||||
|
<option name="channel" value="DEFAULT" />
|
||||||
|
<option name="requiredFeatures" value="true" />
|
||||||
|
<option name="allFeatures" value="false" />
|
||||||
|
<option name="emulateTerminal" value="false" />
|
||||||
|
<option name="withSudo" value="false" />
|
||||||
|
<option name="buildTarget" value="REMOTE" />
|
||||||
|
<option name="backtrace" value="SHORT" />
|
||||||
|
<envs />
|
||||||
|
<option name="isRedirectInput" value="false" />
|
||||||
|
<option name="redirectInputPath" value="" />
|
||||||
|
<method v="2">
|
||||||
|
<option name="CARGO.BUILD_TASK_PROVIDER" enabled="true" />
|
||||||
|
</method>
|
||||||
|
</configuration>
|
||||||
|
</component>
|
||||||
+125
-136
@@ -31,13 +31,6 @@
|
|||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate lazy_static;
|
extern crate lazy_static;
|
||||||
|
|
||||||
#[cfg(all(target_os = "windows", windows))]
|
|
||||||
use std::{
|
|
||||||
borrow::{Borrow, Cow},
|
|
||||||
cmp::Ordering,
|
|
||||||
slice::from_raw_parts,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[cfg(all(windows, not(feature = "docs-only")))]
|
#[cfg(all(windows, not(feature = "docs-only")))]
|
||||||
pub mod wmf {
|
pub mod wmf {
|
||||||
use nokhwa_core::error::NokhwaError;
|
use nokhwa_core::error::NokhwaError;
|
||||||
@@ -45,6 +38,7 @@ pub mod wmf {
|
|||||||
ApiBackend, CameraControl, CameraFormat, CameraIndex, CameraInfo, ControlValueDescription,
|
ApiBackend, CameraControl, CameraFormat, CameraIndex, CameraInfo, ControlValueDescription,
|
||||||
ControlValueSetter, FrameFormat, KnownCameraControl, KnownCameraControlFlag, Resolution,
|
ControlValueSetter, FrameFormat, KnownCameraControl, KnownCameraControlFlag, Resolution,
|
||||||
};
|
};
|
||||||
|
use std::ffi::c_void;
|
||||||
use std::{
|
use std::{
|
||||||
borrow::Cow,
|
borrow::Cow,
|
||||||
cell::Cell,
|
cell::Cell,
|
||||||
@@ -65,11 +59,11 @@ pub mod wmf {
|
|||||||
Media::{
|
Media::{
|
||||||
DirectShow::{
|
DirectShow::{
|
||||||
CameraControl_Exposure, CameraControl_Focus, CameraControl_Iris,
|
CameraControl_Exposure, CameraControl_Focus, CameraControl_Iris,
|
||||||
CameraControl_Pan, CameraControl_Roll, CameraControl_Tilt, CameraControl_Zoom,
|
CameraControl_Pan, CameraControl_Tilt, CameraControl_Zoom, IAMCameraControl,
|
||||||
IAMCameraControl, IAMVideoProcAmp, VideoProcAmp_BacklightCompensation,
|
IAMVideoProcAmp, VideoProcAmp_BacklightCompensation, VideoProcAmp_Brightness,
|
||||||
VideoProcAmp_Brightness, VideoProcAmp_ColorEnable, VideoProcAmp_Contrast,
|
VideoProcAmp_ColorEnable, VideoProcAmp_Contrast, VideoProcAmp_Gain,
|
||||||
VideoProcAmp_Gain, VideoProcAmp_Gamma, VideoProcAmp_Hue,
|
VideoProcAmp_Gamma, VideoProcAmp_Hue, VideoProcAmp_Saturation,
|
||||||
VideoProcAmp_Saturation, VideoProcAmp_Sharpness, VideoProcAmp_WhiteBalance,
|
VideoProcAmp_Sharpness, VideoProcAmp_WhiteBalance,
|
||||||
},
|
},
|
||||||
KernelStreaming::GUID_NULL,
|
KernelStreaming::GUID_NULL,
|
||||||
MediaFoundation::{
|
MediaFoundation::{
|
||||||
@@ -120,53 +114,52 @@ pub mod wmf {
|
|||||||
const MEDIA_FOUNDATION_FIRST_VIDEO_STREAM: u32 = 0xFFFF_FFFC;
|
const MEDIA_FOUNDATION_FIRST_VIDEO_STREAM: u32 = 0xFFFF_FFFC;
|
||||||
const MF_SOURCE_READER_MEDIASOURCE: u32 = 0xFFFF_FFFF;
|
const MF_SOURCE_READER_MEDIASOURCE: u32 = 0xFFFF_FFFF;
|
||||||
|
|
||||||
const CAM_CTRL_AUTO: i32 = 0x0001;
|
// const CAM_CTRL_AUTO: i32 = 0x0001;
|
||||||
const CAM_CTRL_MANUAL: i32 = 0x0002;
|
// const CAM_CTRL_MANUAL: i32 = 0x0002;
|
||||||
|
|
||||||
macro_rules! define_controls {
|
// macro_rules! define_controls {
|
||||||
( $( ($key:expr => ($property:ident, $min:ident, $max:ident, $step:ident, $default:ident, $flag:ident)) )* ) => {
|
// ( $( ($key:expr => ($property:ident, $min:ident, $max:ident, $step:ident, $default:ident, $flag:ident)) )* ) => {
|
||||||
$(
|
// $(
|
||||||
$key => {
|
// $key => {
|
||||||
if let Err(why) = unsafe {
|
// if let Err(why) = unsafe {
|
||||||
video_proc_amp.GetRange(
|
// video_proc_amp.GetRange(
|
||||||
$property.0,
|
// $property.0,
|
||||||
&mut $min,
|
// &mut $min,
|
||||||
&mut $max,
|
// &mut $max,
|
||||||
&mut $step,
|
// &mut $step,
|
||||||
&mut $default,
|
// &mut $default,
|
||||||
&mut $flag,
|
// &mut $flag,
|
||||||
)
|
// )
|
||||||
} {
|
// } {
|
||||||
return Err(NokhwaError::GetPropertyError {
|
// return Err(NokhwaError::GetPropertyError {
|
||||||
property: stringify!($key).to_string(),
|
// property: stringify!($key).to_string(),
|
||||||
error: why.to_string()
|
// error: why.to_string()
|
||||||
});
|
// });
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
)*
|
// )*
|
||||||
};
|
// };
|
||||||
( $( ($key:expr : ($property:ident, $value:ident, $flag:ident)) )* ) => {
|
// ( $( ($key:expr : ($property:ident, $value:ident, $flag:ident)) )* ) => {
|
||||||
$(
|
// $(
|
||||||
$key => {
|
// $key => {
|
||||||
if let Err(why) = unsafe {
|
// if let Err(why) = unsafe {
|
||||||
video_proc_amp.Get($property.0, &mut $value, &mut $flag)
|
// video_proc_amp.Get($property.0, &mut $value, &mut $flag)
|
||||||
} {
|
// } {
|
||||||
return Err(NokhwaError::GetPropertyError {
|
// return Err(NokhwaError::GetPropertyError {
|
||||||
property: stringify!($key).to_string(),
|
// property: stringify!($key).to_string(),
|
||||||
error: why.to_string()
|
// error: why.to_string()
|
||||||
});
|
// });
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
)*
|
// )*
|
||||||
};
|
// };
|
||||||
}
|
// }
|
||||||
|
|
||||||
pub fn initialize_mf() -> Result<(), NokhwaError> {
|
pub fn initialize_mf() -> Result<(), NokhwaError> {
|
||||||
if !(INITIALIZED.load(Ordering::SeqCst)) {
|
if !(INITIALIZED.load(Ordering::SeqCst)) {
|
||||||
let a = std::ptr::null_mut();
|
if let Err(why) = unsafe {
|
||||||
if let Err(why) =
|
CoInitializeEx(None, CO_INIT_APARTMENT_THREADED | CO_INIT_DISABLE_OLE1DDE)
|
||||||
unsafe { CoInitializeEx(a, CO_INIT_APARTMENT_THREADED | CO_INIT_DISABLE_OLE1DDE) }
|
} {
|
||||||
{
|
|
||||||
return Err(NokhwaError::InitializeError {
|
return Err(NokhwaError::InitializeError {
|
||||||
backend: ApiBackend::MediaFoundation,
|
backend: ApiBackend::MediaFoundation,
|
||||||
error: why.to_string(),
|
error: why.to_string(),
|
||||||
@@ -270,15 +263,15 @@ pub mod wmf {
|
|||||||
imf_activate: &IMFActivate,
|
imf_activate: &IMFActivate,
|
||||||
) -> Result<CameraInfo, NokhwaError> {
|
) -> Result<CameraInfo, NokhwaError> {
|
||||||
let mut pwstr_name = PWSTR(&mut 0_u16);
|
let mut pwstr_name = PWSTR(&mut 0_u16);
|
||||||
let mut _len_pwstrname = 0;
|
let mut len_pwstrname = 0;
|
||||||
let mut pwstr_symlink = PWSTR(&mut 0_u16);
|
let mut pwstr_symlink = PWSTR(&mut 0_u16);
|
||||||
let mut _len_pwstrsymlink = 0;
|
let mut len_pwstrsymlink = 0;
|
||||||
|
|
||||||
if let Err(why) = unsafe {
|
if let Err(why) = unsafe {
|
||||||
imf_activate.GetAllocatedString(
|
imf_activate.GetAllocatedString(
|
||||||
&MF_DEVSOURCE_ATTRIBUTE_FRIENDLY_NAME,
|
&MF_DEVSOURCE_ATTRIBUTE_FRIENDLY_NAME,
|
||||||
&mut pwstr_name,
|
&mut pwstr_name,
|
||||||
&mut _len_pwstrname,
|
&mut len_pwstrname,
|
||||||
)
|
)
|
||||||
} {
|
} {
|
||||||
return Err(NokhwaError::GetPropertyError {
|
return Err(NokhwaError::GetPropertyError {
|
||||||
@@ -291,7 +284,7 @@ pub mod wmf {
|
|||||||
imf_activate.GetAllocatedString(
|
imf_activate.GetAllocatedString(
|
||||||
&MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK,
|
&MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK,
|
||||||
&mut pwstr_symlink,
|
&mut pwstr_symlink,
|
||||||
&mut _len_pwstrsymlink,
|
&mut len_pwstrsymlink,
|
||||||
)
|
)
|
||||||
} {
|
} {
|
||||||
return Err(NokhwaError::GetPropertyError {
|
return Err(NokhwaError::GetPropertyError {
|
||||||
@@ -358,7 +351,8 @@ pub mod wmf {
|
|||||||
CCRange(i32),
|
CCRange(i32),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn kcc_to_i32(kcc: KnownCameraControl) -> Option<MFControlId> {
|
#[allow(clippy::cast_sign_loss)]
|
||||||
|
fn kcc_to_i32(kcc: KnownCameraControl) -> Option<MFControlId> {
|
||||||
let control_id = match kcc {
|
let control_id = match kcc {
|
||||||
KnownCameraControl::Brightness => MFControlId::ProcAmpRange(VideoProcAmp_Brightness.0),
|
KnownCameraControl::Brightness => MFControlId::ProcAmpRange(VideoProcAmp_Brightness.0),
|
||||||
KnownCameraControl::Contrast => MFControlId::ProcAmpRange(VideoProcAmp_Contrast.0),
|
KnownCameraControl::Contrast => MFControlId::ProcAmpRange(VideoProcAmp_Contrast.0),
|
||||||
@@ -380,7 +374,7 @@ pub mod wmf {
|
|||||||
KnownCameraControl::Iris => MFControlId::CCValue(CameraControl_Iris.0),
|
KnownCameraControl::Iris => MFControlId::CCValue(CameraControl_Iris.0),
|
||||||
KnownCameraControl::Focus => MFControlId::CCValue(CameraControl_Focus.0),
|
KnownCameraControl::Focus => MFControlId::CCValue(CameraControl_Focus.0),
|
||||||
KnownCameraControl::Other(o) => {
|
KnownCameraControl::Other(o) => {
|
||||||
if o == VideoProcAmp_ColorEnable as u128 {
|
if o == VideoProcAmp_ColorEnable.0 as u128 {
|
||||||
MFControlId::ProcAmpRange(o as i32)
|
MFControlId::ProcAmpRange(o as i32)
|
||||||
} else {
|
} else {
|
||||||
return None;
|
return None;
|
||||||
@@ -391,14 +385,14 @@ pub mod wmf {
|
|||||||
Some(control_id)
|
Some(control_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct MediaFoundationDevice<'a> {
|
pub struct MediaFoundationDevice {
|
||||||
is_open: Cell<bool>,
|
is_open: Cell<bool>,
|
||||||
device_specifier: CameraInfo,
|
device_specifier: CameraInfo,
|
||||||
device_format: CameraFormat,
|
device_format: CameraFormat,
|
||||||
source_reader: IMFSourceReader,
|
source_reader: IMFSourceReader,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> MediaFoundationDevice<'a> {
|
impl MediaFoundationDevice {
|
||||||
pub fn new(index: CameraIndex) -> Result<Self, NokhwaError> {
|
pub fn new(index: CameraIndex) -> Result<Self, NokhwaError> {
|
||||||
match index {
|
match index {
|
||||||
CameraIndex::Index(i) => {
|
CameraIndex::Index(i) => {
|
||||||
@@ -477,7 +471,7 @@ pub mod wmf {
|
|||||||
Ok(MediaFoundationDevice {
|
Ok(MediaFoundationDevice {
|
||||||
is_open: Cell::new(false),
|
is_open: Cell::new(false),
|
||||||
device_specifier: device_descriptor,
|
device_specifier: device_descriptor,
|
||||||
device_format: MFCameraFormat::default(),
|
device_format: CameraFormat::default(),
|
||||||
source_reader,
|
source_reader,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -494,9 +488,7 @@ pub mod wmf {
|
|||||||
|
|
||||||
match id_eq {
|
match id_eq {
|
||||||
Some(index) => Self::new(CameraIndex::Index(index)),
|
Some(index) => Self::new(CameraIndex::Index(index)),
|
||||||
None => {
|
None => Err(NokhwaError::OpenDeviceError(s, "Not Found".to_string())),
|
||||||
return Err(NokhwaError::OpenDeviceError(s, "Not Found".to_string()))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -673,13 +665,15 @@ pub mod wmf {
|
|||||||
MF_SOURCE_READER_MEDIASOURCE,
|
MF_SOURCE_READER_MEDIASOURCE,
|
||||||
&GUID_NULL,
|
&GUID_NULL,
|
||||||
&IAMCameraControl::IID,
|
&IAMCameraControl::IID,
|
||||||
(ptr_receiver as *mut IAMCameraControl).cast::<*mut std::ffi::c_void>(),
|
ptr_receiver
|
||||||
|
.cast::<IAMCameraControl>()
|
||||||
|
.cast::<*mut c_void>(),
|
||||||
) {
|
) {
|
||||||
return Err(BindingError::GUIDSetError(
|
return Err(NokhwaError::SetPropertyError {
|
||||||
"MF_SOURCE_READER_MEDIASOURCE".to_string(),
|
property: "MF_SOURCE_READER_MEDIASOURCE".to_string(),
|
||||||
"IAMCameraControl".to_string(),
|
value: "IAMCameraControl".to_string(),
|
||||||
why.to_string(),
|
error: why.to_string(),
|
||||||
));
|
});
|
||||||
}
|
}
|
||||||
receiver.assume_init()
|
receiver.assume_init()
|
||||||
};
|
};
|
||||||
@@ -690,13 +684,13 @@ pub mod wmf {
|
|||||||
MF_SOURCE_READER_MEDIASOURCE,
|
MF_SOURCE_READER_MEDIASOURCE,
|
||||||
&GUID_NULL,
|
&GUID_NULL,
|
||||||
&IAMVideoProcAmp::IID,
|
&IAMVideoProcAmp::IID,
|
||||||
(ptr_receiver as *mut IAMVideoProcAmp).cast::<*mut std::ffi::c_void>(),
|
ptr_receiver.cast::<IAMVideoProcAmp>().cast::<*mut c_void>(),
|
||||||
) {
|
) {
|
||||||
return Err(BindingError::GUIDSetError(
|
return Err(NokhwaError::SetPropertyError {
|
||||||
"MF_SOURCE_READER_MEDIASOURCE".to_string(),
|
property: "MF_SOURCE_READER_MEDIASOURCE".to_string(),
|
||||||
"IAMVideoProcAmp".to_string(),
|
value: "IAMVideoProcAmp".to_string(),
|
||||||
why.to_string(),
|
error: why.to_string(),
|
||||||
));
|
});
|
||||||
}
|
}
|
||||||
receiver.assume_init()
|
receiver.assume_init()
|
||||||
};
|
};
|
||||||
@@ -764,11 +758,11 @@ pub mod wmf {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
ControlValueDescription::IntegerRange {
|
ControlValueDescription::IntegerRange {
|
||||||
min: min as i64,
|
min: i64::from(min),
|
||||||
max: max as i64,
|
max: i64::from(max),
|
||||||
value: value as i64,
|
value: i64::from(value),
|
||||||
step: step as i64,
|
step: i64::from(step),
|
||||||
default: default as i64,
|
default: i64::from(default),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
MFControlId::CCValue(id) => unsafe {
|
MFControlId::CCValue(id) => unsafe {
|
||||||
@@ -793,9 +787,9 @@ pub mod wmf {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ControlValueDescription::Integer {
|
ControlValueDescription::Integer {
|
||||||
value: value as i64,
|
value: i64::from(value),
|
||||||
default: default as i64,
|
default: i64::from(default),
|
||||||
step: step as i64,
|
step: i64::from(step),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
MFControlId::CCRange(id) => unsafe {
|
MFControlId::CCRange(id) => unsafe {
|
||||||
@@ -819,16 +813,16 @@ pub mod wmf {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
ControlValueDescription::IntegerRange {
|
ControlValueDescription::IntegerRange {
|
||||||
min: min as i64,
|
min: i64::from(min),
|
||||||
max: max as i64,
|
max: i64::from(max),
|
||||||
value: value as i64,
|
value: i64::from(value),
|
||||||
step: step as i64,
|
step: i64::from(step),
|
||||||
default: default as i64,
|
default: i64::from(default),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
let is_manual = if matches!(flag, CameraControl_Flags_Manual) {
|
let is_manual = if flag == CameraControl_Flags_Manual.0 {
|
||||||
KnownCameraControlFlag::Manual
|
KnownCameraControlFlag::Manual
|
||||||
} else {
|
} else {
|
||||||
KnownCameraControlFlag::Automatic
|
KnownCameraControlFlag::Automatic
|
||||||
@@ -857,13 +851,15 @@ pub mod wmf {
|
|||||||
MF_SOURCE_READER_MEDIASOURCE,
|
MF_SOURCE_READER_MEDIASOURCE,
|
||||||
&GUID_NULL,
|
&GUID_NULL,
|
||||||
&IAMCameraControl::IID,
|
&IAMCameraControl::IID,
|
||||||
(ptr_receiver as *mut IAMCameraControl).cast::<*mut std::ffi::c_void>(),
|
ptr_receiver
|
||||||
|
.cast::<IAMCameraControl>()
|
||||||
|
.cast::<*mut c_void>(),
|
||||||
) {
|
) {
|
||||||
return Err(BindingError::GUIDSetError(
|
return Err(NokhwaError::SetPropertyError {
|
||||||
"MF_SOURCE_READER_MEDIASOURCE".to_string(),
|
property: "MF_SOURCE_READER_MEDIASOURCE".to_string(),
|
||||||
"IAMCameraControl".to_string(),
|
value: "IAMCameraControl".to_string(),
|
||||||
why.to_string(),
|
error: why.to_string(),
|
||||||
));
|
});
|
||||||
}
|
}
|
||||||
receiver.assume_init()
|
receiver.assume_init()
|
||||||
};
|
};
|
||||||
@@ -874,13 +870,13 @@ pub mod wmf {
|
|||||||
MF_SOURCE_READER_MEDIASOURCE,
|
MF_SOURCE_READER_MEDIASOURCE,
|
||||||
&GUID_NULL,
|
&GUID_NULL,
|
||||||
&IAMVideoProcAmp::IID,
|
&IAMVideoProcAmp::IID,
|
||||||
(ptr_receiver as *mut IAMVideoProcAmp).cast::<*mut std::ffi::c_void>(),
|
ptr_receiver.cast::<IAMVideoProcAmp>().cast::<*mut c_void>(),
|
||||||
) {
|
) {
|
||||||
return Err(BindingError::GUIDSetError(
|
return Err(NokhwaError::SetPropertyError {
|
||||||
"MF_SOURCE_READER_MEDIASOURCE".to_string(),
|
property: "MF_SOURCE_READER_MEDIASOURCE".to_string(),
|
||||||
"IAMVideoProcAmp".to_string(),
|
value: "IAMVideoProcAmp".to_string(),
|
||||||
why.to_string(),
|
error: why.to_string(),
|
||||||
));
|
});
|
||||||
}
|
}
|
||||||
receiver.assume_init()
|
receiver.assume_init()
|
||||||
};
|
};
|
||||||
@@ -893,7 +889,7 @@ pub mod wmf {
|
|||||||
|
|
||||||
let ctrl_value = match value {
|
let ctrl_value = match value {
|
||||||
ControlValueSetter::Integer(i) => i as i32,
|
ControlValueSetter::Integer(i) => i as i32,
|
||||||
ControlValueSetter::Boolean(b) => b as i32,
|
ControlValueSetter::Boolean(b) => i32::from(b),
|
||||||
v => {
|
v => {
|
||||||
return Err(NokhwaError::StructureError {
|
return Err(NokhwaError::StructureError {
|
||||||
structure: format!("ControlValueSetter {}", v),
|
structure: format!("ControlValueSetter {}", v),
|
||||||
@@ -906,7 +902,7 @@ pub mod wmf {
|
|||||||
.flag()
|
.flag()
|
||||||
.get(0)
|
.get(0)
|
||||||
.map(|x| {
|
.map(|x| {
|
||||||
if x == KnownCameraControlFlag::Automatic {
|
if *x == KnownCameraControlFlag::Automatic {
|
||||||
CameraControl_Flags_Auto
|
CameraControl_Flags_Auto
|
||||||
} else {
|
} else {
|
||||||
CameraControl_Flags_Manual
|
CameraControl_Flags_Manual
|
||||||
@@ -941,6 +937,7 @@ pub mod wmf {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::cast_sign_loss)]
|
||||||
pub fn format_refreshed(&mut self) -> Result<CameraFormat, NokhwaError> {
|
pub fn format_refreshed(&mut self) -> Result<CameraFormat, NokhwaError> {
|
||||||
match unsafe {
|
match unsafe {
|
||||||
self.source_reader
|
self.source_reader
|
||||||
@@ -983,7 +980,7 @@ pub mod wmf {
|
|||||||
_ => {
|
_ => {
|
||||||
return Err(NokhwaError::GetPropertyError {
|
return Err(NokhwaError::GetPropertyError {
|
||||||
property: "MF_MT_SUBTYPE".to_string(),
|
property: "MF_MT_SUBTYPE".to_string(),
|
||||||
error: why.to_string(),
|
error: "Unknown".to_string(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -1038,6 +1035,11 @@ pub mod wmf {
|
|||||||
FrameFormat::MJPEG => MF_VIDEO_FORMAT_MJPEG,
|
FrameFormat::MJPEG => MF_VIDEO_FORMAT_MJPEG,
|
||||||
FrameFormat::YUYV => MF_VIDEO_FORMAT_YUY2,
|
FrameFormat::YUYV => MF_VIDEO_FORMAT_YUY2,
|
||||||
FrameFormat::GRAY => MF_VIDEO_FORMAT_GRAY,
|
FrameFormat::GRAY => MF_VIDEO_FORMAT_GRAY,
|
||||||
|
FrameFormat::RAWRGB => {
|
||||||
|
return Err(NokhwaError::NotImplementedError(
|
||||||
|
"RGB24 not implemented".to_string(),
|
||||||
|
))
|
||||||
|
} // TODO: Implement RGB24
|
||||||
};
|
};
|
||||||
// setting to the new media_type
|
// setting to the new media_type
|
||||||
if let Err(why) = unsafe { media_type.SetGUID(&MF_MT_MAJOR_TYPE, &MFMediaType_Video) } {
|
if let Err(why) = unsafe { media_type.SetGUID(&MF_MT_MAJOR_TYPE, &MFMediaType_Video) } {
|
||||||
@@ -1050,7 +1052,7 @@ pub mod wmf {
|
|||||||
if let Err(why) = unsafe { media_type.SetGUID(&MF_MT_SUBTYPE, &fourcc) } {
|
if let Err(why) = unsafe { media_type.SetGUID(&MF_MT_SUBTYPE, &fourcc) } {
|
||||||
return Err(NokhwaError::SetPropertyError {
|
return Err(NokhwaError::SetPropertyError {
|
||||||
property: "MF_MT_SUBTYPE".to_string(),
|
property: "MF_MT_SUBTYPE".to_string(),
|
||||||
value: fourcc.to_string(),
|
value: format!("{:?}", fourcc),
|
||||||
error: why.to_string(),
|
error: why.to_string(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -1083,11 +1085,10 @@ pub mod wmf {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let reserved = std::ptr::null_mut();
|
|
||||||
if let Err(why) = unsafe {
|
if let Err(why) = unsafe {
|
||||||
self.source_reader.SetCurrentMediaType(
|
self.source_reader.SetCurrentMediaType(
|
||||||
MEDIA_FOUNDATION_FIRST_VIDEO_STREAM,
|
MEDIA_FOUNDATION_FIRST_VIDEO_STREAM,
|
||||||
reserved,
|
None,
|
||||||
&media_type,
|
&media_type,
|
||||||
)
|
)
|
||||||
} {
|
} {
|
||||||
@@ -1118,20 +1119,18 @@ pub mod wmf {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn raw_bytes(&mut self) -> Result<Cow<'a, [u8]>, NokhwaError> {
|
pub fn raw_bytes(&mut self) -> Result<Cow<[u8]>, NokhwaError> {
|
||||||
let mut flags: u32 = 0;
|
|
||||||
let mut imf_sample: Option<IMFSample> = None;
|
let mut imf_sample: Option<IMFSample> = None;
|
||||||
|
|
||||||
{
|
{
|
||||||
loop {
|
loop {
|
||||||
if let Err(why) = unsafe {
|
if let Err(why) = unsafe {
|
||||||
self.source_reader.ReadSample(
|
self.source_reader.ReadSample(
|
||||||
MEDIA_FOUNDATION_FIRST_VIDEO_STREAM,
|
MEDIA_FOUNDATION_FIRST_VIDEO_STREAM,
|
||||||
0,
|
0,
|
||||||
std::ptr::null_mut(),
|
None,
|
||||||
&mut flags,
|
None,
|
||||||
std::ptr::null_mut(),
|
None,
|
||||||
&mut imf_sample,
|
Some(&mut imf_sample),
|
||||||
)
|
)
|
||||||
} {
|
} {
|
||||||
return Err(NokhwaError::ReadFrameError(why.to_string()));
|
return Err(NokhwaError::ReadFrameError(why.to_string()));
|
||||||
@@ -1147,7 +1146,7 @@ pub mod wmf {
|
|||||||
Some(sample) => sample,
|
Some(sample) => sample,
|
||||||
None => {
|
None => {
|
||||||
// shouldn't happen
|
// shouldn't happen
|
||||||
return Err(NokhwaError::ReadFrameError(why.to_string()));
|
return Err(NokhwaError::ReadFrameError("No sample".to_string()));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1159,13 +1158,9 @@ pub mod wmf {
|
|||||||
let mut buffer_valid_length = 0;
|
let mut buffer_valid_length = 0;
|
||||||
let mut buffer_start_ptr = std::ptr::null_mut::<u8>();
|
let mut buffer_start_ptr = std::ptr::null_mut::<u8>();
|
||||||
|
|
||||||
if let Err(why) = unsafe {
|
if let Err(why) =
|
||||||
buffer.Lock(
|
unsafe { buffer.Lock(&mut buffer_start_ptr, None, Some(&mut buffer_valid_length)) }
|
||||||
&mut buffer_start_ptr,
|
{
|
||||||
std::ptr::null_mut(),
|
|
||||||
&mut buffer_valid_length,
|
|
||||||
)
|
|
||||||
} {
|
|
||||||
return Err(NokhwaError::ReadFrameError(why.to_string()));
|
return Err(NokhwaError::ReadFrameError(why.to_string()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1189,11 +1184,7 @@ pub mod wmf {
|
|||||||
) as &[u8]);
|
) as &[u8]);
|
||||||
// swallow errors
|
// swallow errors
|
||||||
if buffer
|
if buffer
|
||||||
.Lock(
|
.Lock(&mut buffer_start_ptr, None, Some(&mut buffer_valid_length))
|
||||||
&mut buffer_start_ptr,
|
|
||||||
std::ptr::null_mut(),
|
|
||||||
&mut buffer_valid_length,
|
|
||||||
)
|
|
||||||
.is_ok()
|
.is_ok()
|
||||||
{}
|
{}
|
||||||
}
|
}
|
||||||
@@ -1206,7 +1197,7 @@ pub mod wmf {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Drop for MediaFoundationDevice<'a> {
|
impl Drop for MediaFoundationDevice {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
// swallow errors
|
// swallow errors
|
||||||
unsafe {
|
unsafe {
|
||||||
@@ -1262,15 +1253,13 @@ pub mod wmf {
|
|||||||
|
|
||||||
struct Empty;
|
struct Empty;
|
||||||
|
|
||||||
pub struct MediaFoundationDevice<'a> {
|
pub struct MediaFoundationDevice {
|
||||||
_phantom: &'a Empty,
|
|
||||||
camera: CameraIndex,
|
camera: CameraIndex,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> MediaFoundationDevice<'a> {
|
impl MediaFoundationDevice {
|
||||||
pub fn new(_index: CameraIndex) -> Result<Self, NokhwaError> {
|
pub fn new(_index: CameraIndex) -> Result<Self, NokhwaError> {
|
||||||
Ok(MediaFoundationDevice {
|
Ok(MediaFoundationDevice {
|
||||||
_phantom: &Empty,
|
|
||||||
camera: CameraIndex::Index(0),
|
camera: CameraIndex::Index(0),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,11 +17,12 @@ use nokhwa_bindings_windows::wmf::MediaFoundationDevice;
|
|||||||
use nokhwa_core::{
|
use nokhwa_core::{
|
||||||
buffer::Buffer,
|
buffer::Buffer,
|
||||||
error::NokhwaError,
|
error::NokhwaError,
|
||||||
|
pixel_format::RgbFormat,
|
||||||
traits::CaptureBackendTrait,
|
traits::CaptureBackendTrait,
|
||||||
types::{
|
types::{
|
||||||
all_known_camera_controls, ApiBackend, CameraControl, CameraFormat, CameraIndex,
|
all_known_camera_controls, ApiBackend, CameraControl, CameraFormat, CameraIndex,
|
||||||
CameraInfo, ControlValueSetter, FrameFormat, KnownCameraControl, RequestedFormat,
|
CameraInfo, ControlValueSetter, FrameFormat, KnownCameraControl, RequestedFormat,
|
||||||
Resolution,
|
RequestedFormatType, Resolution,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use std::{borrow::Cow, collections::HashMap};
|
use std::{borrow::Cow, collections::HashMap};
|
||||||
@@ -38,12 +39,12 @@ use std::{borrow::Cow, collections::HashMap};
|
|||||||
/// - The names may contain invalid characters since they were converted from UTF16.
|
/// - The names may contain invalid characters since they were converted from UTF16.
|
||||||
/// - When you call new or drop the struct, `initialize`/`de_initialize` will automatically be called.
|
/// - When you call new or drop the struct, `initialize`/`de_initialize` will automatically be called.
|
||||||
#[cfg_attr(feature = "docs-features", doc(cfg(feature = "input-msmf")))]
|
#[cfg_attr(feature = "docs-features", doc(cfg(feature = "input-msmf")))]
|
||||||
pub struct MediaFoundationCaptureDevice<'a> {
|
pub struct MediaFoundationCaptureDevice {
|
||||||
inner: MediaFoundationDevice<'a>,
|
inner: MediaFoundationDevice,
|
||||||
info: CameraInfo,
|
info: CameraInfo,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> MediaFoundationCaptureDevice<'a> {
|
impl MediaFoundationCaptureDevice {
|
||||||
/// Creates a new capture device using the Media Foundation backend. Indexes are gives to devices by the OS, and usually numbered by order of discovery.
|
/// Creates a new capture device using the Media Foundation backend. Indexes are gives to devices by the OS, and usually numbered by order of discovery.
|
||||||
/// # Errors
|
/// # Errors
|
||||||
/// This function will error if Media Foundation fails to get the device.
|
/// This function will error if Media Foundation fails to get the device.
|
||||||
@@ -52,28 +53,21 @@ impl<'a> MediaFoundationCaptureDevice<'a> {
|
|||||||
|
|
||||||
let info = CameraInfo::new(
|
let info = CameraInfo::new(
|
||||||
&mf_device.name(),
|
&mf_device.name(),
|
||||||
&"MediaFoundation Camera Device".to_string(),
|
"MediaFoundation Camera Device",
|
||||||
&mf_device.symlink(),
|
&mf_device.symlink(),
|
||||||
index.clone(),
|
index.clone(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let availible = mf_device
|
let availible = mf_device.compatible_format_list()?;
|
||||||
.compatible_format_list()?
|
|
||||||
.into_iter()
|
|
||||||
.map(|x| {
|
|
||||||
let cf: CameraFormat = x.into();
|
|
||||||
cf
|
|
||||||
})
|
|
||||||
.collect::<Vec<CameraFormat>>();
|
|
||||||
|
|
||||||
let desired = camera_fmt
|
let desired = camera_fmt
|
||||||
.fufill(&availible)
|
.fulfill(&availible)
|
||||||
.ok_or(NokhwaError::InitializeError {
|
.ok_or(NokhwaError::InitializeError {
|
||||||
backend: ApiBackend::MediaFoundation,
|
backend: ApiBackend::MediaFoundation,
|
||||||
error: "Failed to fulfill requested format".to_string(),
|
error: "Failed to fulfill requested format".to_string(),
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
mf_device.set_format(desired.into())?;
|
mf_device.set_format(desired)?;
|
||||||
|
|
||||||
let mut new_cam = MediaFoundationCaptureDevice {
|
let mut new_cam = MediaFoundationCaptureDevice {
|
||||||
inner: mf_device,
|
inner: mf_device,
|
||||||
@@ -86,7 +80,7 @@ impl<'a> MediaFoundationCaptureDevice<'a> {
|
|||||||
/// Create a new Media Foundation Device with desired settings.
|
/// Create a new Media Foundation Device with desired settings.
|
||||||
/// # Errors
|
/// # Errors
|
||||||
/// This function will error if Media Foundation fails to get the device.
|
/// This function will error if Media Foundation fails to get the device.
|
||||||
#[deprecated(since = "0.10", note = "please use `new` instead.")]
|
#[deprecated(since = "0.10.0", note = "please use `new` instead.")]
|
||||||
pub fn new_with(
|
pub fn new_with(
|
||||||
index: &CameraIndex,
|
index: &CameraIndex,
|
||||||
width: u32,
|
width: u32,
|
||||||
@@ -94,51 +88,28 @@ impl<'a> MediaFoundationCaptureDevice<'a> {
|
|||||||
fps: u32,
|
fps: u32,
|
||||||
fourcc: FrameFormat,
|
fourcc: FrameFormat,
|
||||||
) -> Result<Self, NokhwaError> {
|
) -> Result<Self, NokhwaError> {
|
||||||
let camera_format =
|
let camera_format = RequestedFormat::new::<RgbFormat>(RequestedFormatType::Exact(
|
||||||
RequestedFormat::Exact(CameraFormat::new_from(width, height, fourcc, fps));
|
CameraFormat::new_from(width, height, fourcc, fps),
|
||||||
|
));
|
||||||
MediaFoundationCaptureDevice::new(index, camera_format)
|
MediaFoundationCaptureDevice::new(index, camera_format)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the list of supported [`KnownCameraControl`]s
|
/// Gets the list of supported [`KnownCameraControl`]s
|
||||||
/// # Errors
|
/// # Errors
|
||||||
/// May error if there is an error from `MediaFoundation`.
|
/// May error if there is an error from `MediaFoundation`.
|
||||||
fn supported_camera_controls(&self) -> Result<Vec<KnownCameraControl>, NokhwaError> {
|
pub fn supported_camera_controls(&self) -> Vec<KnownCameraControl> {
|
||||||
let mut supported_camera_controls: Vec<KnownCameraControl> = vec![];
|
let mut supported_camera_controls: Vec<KnownCameraControl> = vec![];
|
||||||
|
|
||||||
for camera_control in all_known_camera_controls() {
|
for camera_control in all_known_camera_controls() {
|
||||||
let msmf_camera_control: MediaFoundationControls = match camera_control {
|
if let Ok(supported) = self.inner.control(camera_control) {
|
||||||
KnownCameraControl::Brightness => MediaFoundationControls::Brightness,
|
supported_camera_controls.push(supported.control());
|
||||||
KnownCameraControl::Contrast => MediaFoundationControls::Contrast,
|
|
||||||
KnownCameraControl::Hue => MediaFoundationControls::Hue,
|
|
||||||
KnownCameraControl::Saturation => MediaFoundationControls::Saturation,
|
|
||||||
KnownCameraControl::Sharpness => MediaFoundationControls::Sharpness,
|
|
||||||
KnownCameraControl::Gamma => MediaFoundationControls::Gamma,
|
|
||||||
KnownCameraControl::WhiteBalance => MediaFoundationControls::WhiteBalance,
|
|
||||||
KnownCameraControl::BacklightComp => MediaFoundationControls::BacklightComp,
|
|
||||||
KnownCameraControl::Gain => MediaFoundationControls::Gain,
|
|
||||||
KnownCameraControl::Pan => MediaFoundationControls::Pan,
|
|
||||||
KnownCameraControl::Tilt => MediaFoundationControls::Tilt,
|
|
||||||
KnownCameraControl::Zoom => MediaFoundationControls::Zoom,
|
|
||||||
KnownCameraControl::Exposure => MediaFoundationControls::Exposure,
|
|
||||||
KnownCameraControl::Iris => MediaFoundationControls::Iris,
|
|
||||||
KnownCameraControl::Focus => MediaFoundationControls::Focus,
|
|
||||||
KnownCameraControl::Other(id) => match id {
|
|
||||||
0 => MediaFoundationControls::ColorEnable,
|
|
||||||
1 => MediaFoundationControls::Roll,
|
|
||||||
_ => continue,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Ok(supported) = self.inner.control(msmf_camera_control) {
|
|
||||||
supported_camera_controls.push(supported.control().into());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
supported_camera_controls
|
||||||
Ok(supported_camera_controls)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> CaptureBackendTrait for MediaFoundationCaptureDevice<'a> {
|
impl CaptureBackendTrait for MediaFoundationCaptureDevice {
|
||||||
fn backend(&self) -> ApiBackend {
|
fn backend(&self) -> ApiBackend {
|
||||||
ApiBackend::MediaFoundation
|
ApiBackend::MediaFoundation
|
||||||
}
|
}
|
||||||
@@ -153,14 +124,11 @@ impl<'a> CaptureBackendTrait for MediaFoundationCaptureDevice<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn camera_format(&self) -> CameraFormat {
|
fn camera_format(&self) -> CameraFormat {
|
||||||
self.inner.format().into()
|
self.inner.format()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_camera_format(&mut self, new_fmt: CameraFormat) -> Result<(), NokhwaError> {
|
fn set_camera_format(&mut self, new_fmt: CameraFormat) -> Result<(), NokhwaError> {
|
||||||
if let Err(why) = self.inner.set_format(new_fmt.into()) {
|
self.inner.set_format(new_fmt)
|
||||||
return Err(why.into());
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compatible_list_by_resolution(
|
fn compatible_list_by_resolution(
|
||||||
@@ -170,9 +138,7 @@ impl<'a> CaptureBackendTrait for MediaFoundationCaptureDevice<'a> {
|
|||||||
let mf_camera_format_list = self.inner.compatible_format_list()?;
|
let mf_camera_format_list = self.inner.compatible_format_list()?;
|
||||||
let mut resolution_map: HashMap<Resolution, Vec<u32>> = HashMap::new();
|
let mut resolution_map: HashMap<Resolution, Vec<u32>> = HashMap::new();
|
||||||
|
|
||||||
for mf_camera_format in mf_camera_format_list {
|
for camera_format in mf_camera_format_list {
|
||||||
let camera_format: CameraFormat = mf_camera_format.into();
|
|
||||||
|
|
||||||
// check fcc
|
// check fcc
|
||||||
if camera_format.format() != fourcc {
|
if camera_format.format() != fourcc {
|
||||||
continue;
|
continue;
|
||||||
@@ -199,9 +165,7 @@ impl<'a> CaptureBackendTrait for MediaFoundationCaptureDevice<'a> {
|
|||||||
let mf_camera_format_list = self.inner.compatible_format_list()?;
|
let mf_camera_format_list = self.inner.compatible_format_list()?;
|
||||||
let mut frame_format_list = vec![];
|
let mut frame_format_list = vec![];
|
||||||
|
|
||||||
for mf_camera_format in mf_camera_format_list {
|
for camera_format in mf_camera_format_list {
|
||||||
let camera_format: CameraFormat = mf_camera_format.into();
|
|
||||||
|
|
||||||
if !frame_format_list.contains(&camera_format.format()) {
|
if !frame_format_list.contains(&camera_format.format()) {
|
||||||
frame_format_list.push(camera_format.format());
|
frame_format_list.push(camera_format.format());
|
||||||
}
|
}
|
||||||
@@ -271,11 +235,7 @@ impl<'a> CaptureBackendTrait for MediaFoundationCaptureDevice<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn open_stream(&mut self) -> Result<(), NokhwaError> {
|
fn open_stream(&mut self) -> Result<(), NokhwaError> {
|
||||||
if let Err(why) = self.inner.start_stream() {
|
self.inner.start_stream()
|
||||||
return Err(why.into());
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_stream_open(&self) -> bool {
|
fn is_stream_open(&self) -> bool {
|
||||||
|
|||||||
+5
-15
@@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
use nokhwa_core::{
|
use nokhwa_core::{
|
||||||
error::NokhwaError,
|
error::NokhwaError,
|
||||||
types::{ApiBackend, CameraIndex, CameraInfo},
|
types::{ApiBackend, CameraInfo},
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: Update as this goes
|
// TODO: Update as this goes
|
||||||
@@ -207,12 +207,13 @@ fn query_uvc() -> Result<Vec<CameraInfo>, NokhwaError> {
|
|||||||
|
|
||||||
#[cfg(feature = "input-gst")]
|
#[cfg(feature = "input-gst")]
|
||||||
fn query_gstreamer() -> Result<Vec<CameraInfo>, NokhwaError> {
|
fn query_gstreamer() -> Result<Vec<CameraInfo>, NokhwaError> {
|
||||||
use crate::CameraIndex;
|
|
||||||
use gstreamer::{
|
use gstreamer::{
|
||||||
prelude::{DeviceExt, DeviceMonitorExt, DeviceMonitorExtManual},
|
prelude::{DeviceExt, DeviceMonitorExt, DeviceMonitorExtManual},
|
||||||
Caps, DeviceMonitor,
|
Caps, DeviceMonitor,
|
||||||
};
|
};
|
||||||
|
use nokhwa_core::types::CameraIndex;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
if let Err(why) = gstreamer::init() {
|
if let Err(why) = gstreamer::init() {
|
||||||
return Err(NokhwaError::GeneralError(format!(
|
return Err(NokhwaError::GeneralError(format!(
|
||||||
"Failed to init gstreamer: {}",
|
"Failed to init gstreamer: {}",
|
||||||
@@ -270,19 +271,8 @@ fn query_gstreamer() -> Result<Vec<CameraInfo>, NokhwaError> {
|
|||||||
|
|
||||||
// please refer to https://docs.microsoft.com/en-us/windows/win32/medfound/enumerating-video-capture-devices
|
// please refer to https://docs.microsoft.com/en-us/windows/win32/medfound/enumerating-video-capture-devices
|
||||||
#[cfg(all(feature = "input-msmf", target_os = "windows"))]
|
#[cfg(all(feature = "input-msmf", target_os = "windows"))]
|
||||||
fn query_msmf<'a>() -> Result<Vec<CameraInfo<'a>>, NokhwaError> {
|
fn query_msmf() -> Result<Vec<CameraInfo>, NokhwaError> {
|
||||||
let list: Vec<CameraInfo> =
|
nokhwa_bindings_windows::wmf::query_media_foundation_descriptors()
|
||||||
match nokhwa_bindings_windows::wmf::query_media_foundation_descriptors() {
|
|
||||||
Ok(l) => l
|
|
||||||
.into_iter()
|
|
||||||
.map(|mf_desc| {
|
|
||||||
let camera_info: CameraInfo = mf_desc.into();
|
|
||||||
camera_info
|
|
||||||
})
|
|
||||||
.collect(),
|
|
||||||
Err(why) => return Err(why.into()),
|
|
||||||
};
|
|
||||||
Ok(list)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(any(not(feature = "input-msmf"), not(target_os = "windows")))]
|
#[cfg(any(not(feature = "input-msmf"), not(target_os = "windows")))]
|
||||||
|
|||||||
Reference in New Issue
Block a user