mirror of
https://github.com/l1npengtul/nokhwa.git
synced 2026-07-04 02:27:26 +00:00
fix media foundation format setting
This commit is contained in:
+2
-1
@@ -80,7 +80,8 @@ features = ["clang-runtime"]
|
||||
optional = true
|
||||
|
||||
[dependencies.nokhwa-bindings-windows]
|
||||
version = "0.3.2"
|
||||
#version = "0.3.3"
|
||||
path = "./nokhwa-bindings-windows"
|
||||
optional = true
|
||||
|
||||
[dependencies.nokhwa-bindings-macos]
|
||||
|
||||
@@ -22,7 +22,10 @@ use glium::{
|
||||
IndexBuffer, Surface, Texture2d, VertexBuffer,
|
||||
};
|
||||
use glutin::{event_loop::EventLoop, window::WindowBuilder, ContextBuilder};
|
||||
use nokhwa::{nokhwa_initialize, query_devices, Camera, CaptureAPIBackend, FrameFormat};
|
||||
use nokhwa::{
|
||||
nokhwa_initialize, query_devices, Camera, CameraFormat, CaptureAPIBackend, FrameFormat,
|
||||
Resolution,
|
||||
};
|
||||
use std::time::Instant;
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "nokhwa-bindings-windows"
|
||||
version = "0.3.3"
|
||||
version = "0.3.4"
|
||||
authors = ["l1npengtul"]
|
||||
edition = "2021"
|
||||
license = "Apache-2.0"
|
||||
|
||||
+186
-159
@@ -61,7 +61,7 @@ pub enum BindingError {
|
||||
NotImplementedError,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
|
||||
pub struct MFResolution {
|
||||
pub width_x: u32,
|
||||
pub height_y: u32,
|
||||
@@ -73,7 +73,7 @@ pub enum MFFrameFormat {
|
||||
YUYV,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Hash, PartialEq)]
|
||||
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, Ord, PartialOrd)]
|
||||
pub struct MFCameraFormat {
|
||||
resolution: MFResolution,
|
||||
format: MFFrameFormat,
|
||||
@@ -366,9 +366,11 @@ pub mod wmf {
|
||||
BindingError, MFCameraFormat, MFControl, MFFrameFormat, MFResolution,
|
||||
MediaFoundationControls, MediaFoundationDeviceDescriptor,
|
||||
};
|
||||
use std::ptr::null_mut;
|
||||
use std::{
|
||||
borrow::Cow,
|
||||
cell::Cell,
|
||||
collections::HashMap,
|
||||
mem::MaybeUninit,
|
||||
slice::from_raw_parts,
|
||||
sync::{
|
||||
@@ -390,15 +392,14 @@ pub mod wmf {
|
||||
VideoProcAmp_Saturation, VideoProcAmp_Sharpness, VideoProcAmp_WhiteBalance,
|
||||
},
|
||||
MediaFoundation::{
|
||||
IMFActivate, IMFAttributes, IMFMediaSource, IMFSample, IMFSourceReader,
|
||||
MFCreateAttributes, MFCreateMediaType, MFCreateSourceReaderFromMediaSource,
|
||||
MFEnumDeviceSources, MFMediaType_Video, MFShutdown, MFStartup,
|
||||
MFSTARTUP_NOSOCKET, MF_API_VERSION, MF_DEVSOURCE_ATTRIBUTE_FRIENDLY_NAME,
|
||||
MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE,
|
||||
IMFActivate, IMFAttributes, IMFMediaSource, IMFMediaType, IMFSample,
|
||||
IMFSourceReader, MFCreateAttributes, MFCreateSourceReaderFromMediaSource,
|
||||
MFEnumDeviceSources, MFShutdown, MFStartup, MFSTARTUP_NOSOCKET, MF_API_VERSION,
|
||||
MF_DEVSOURCE_ATTRIBUTE_FRIENDLY_NAME, MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE,
|
||||
MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID,
|
||||
MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK,
|
||||
MF_MEDIASOURCE_SERVICE, MF_MT_FRAME_RATE, MF_MT_FRAME_RATE_RANGE_MAX,
|
||||
MF_MT_FRAME_RATE_RANGE_MIN, MF_MT_FRAME_SIZE, MF_MT_MAJOR_TYPE, MF_MT_SUBTYPE,
|
||||
MF_MT_FRAME_RATE_RANGE_MIN, MF_MT_FRAME_SIZE, MF_MT_SUBTYPE,
|
||||
MF_READWRITE_DISABLE_CONVERTERS,
|
||||
},
|
||||
},
|
||||
@@ -574,7 +575,7 @@ pub mod wmf {
|
||||
}
|
||||
|
||||
impl<'a> MediaFoundationDevice<'a> {
|
||||
pub fn new(index: usize) -> Result<Self, BindingError> {
|
||||
fn internal(index: usize) -> Result<Self, BindingError> {
|
||||
let (media_source, device_descriptor) = match query_activate_pointers()?
|
||||
.into_iter()
|
||||
.nth(index)
|
||||
@@ -646,19 +647,25 @@ pub mod wmf {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn new(index: usize, format: MFCameraFormat) -> Result<Self, BindingError> {
|
||||
let mut camera = MediaFoundationDevice::internal(index)?;
|
||||
camera.set_format(format)?;
|
||||
Ok(camera)
|
||||
}
|
||||
|
||||
pub fn with_string(unique_id: &[u16]) -> Result<Self, BindingError> {
|
||||
let devicelist = query_media_foundation_descriptors()?;
|
||||
let device_list = query_media_foundation_descriptors()?;
|
||||
let mut id_eq = None;
|
||||
|
||||
for mfdev in devicelist {
|
||||
if (mfdev.symlink() as &[u16]) == unique_id {
|
||||
id_eq = Some(mfdev.index);
|
||||
for media_foundation_device in device_list {
|
||||
if (media_foundation_device.symlink() as &[u16]) == unique_id {
|
||||
id_eq = Some(media_foundation_device.index);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
match id_eq {
|
||||
Some(index) => Self::new(index),
|
||||
Some(index) => Self::internal(index),
|
||||
None => {
|
||||
return Err(BindingError::DeviceOpenFailError(
|
||||
std::str::from_utf8(
|
||||
@@ -684,16 +691,24 @@ pub mod wmf {
|
||||
self.device_specifier.link_as_string()
|
||||
}
|
||||
|
||||
pub fn compatible_format_list(&mut self) -> Result<Vec<MFCameraFormat>, BindingError> {
|
||||
let mut camera_format_list = vec![];
|
||||
fn internal_format_list(
|
||||
&mut self,
|
||||
) -> Result<HashMap<MFCameraFormat, IMFMediaType>, BindingError> {
|
||||
let mut camera_format_map = HashMap::new();
|
||||
let mut index = 0;
|
||||
|
||||
while let Ok(media_type) = unsafe {
|
||||
self.source_reader
|
||||
.GetNativeMediaType(MEDIA_FOUNDATION_FIRST_VIDEO_STREAM, index)
|
||||
} {
|
||||
index += 1;
|
||||
|
||||
let fourcc = match unsafe { media_type.GetGUID(&MF_MT_SUBTYPE) } {
|
||||
Ok(fcc) => fcc,
|
||||
Ok(fcc) => match fcc {
|
||||
MF_VIDEO_FORMAT_YUY2 => MFFrameFormat::YUYV,
|
||||
MF_VIDEO_FORMAT_MJPEG => MFFrameFormat::MJPEG,
|
||||
_ => continue,
|
||||
},
|
||||
Err(why) => {
|
||||
return Err(BindingError::GUIDReadError(
|
||||
"MF_MT_SUBTYPE".to_string(),
|
||||
@@ -770,77 +785,60 @@ pub mod wmf {
|
||||
}
|
||||
};
|
||||
|
||||
if fourcc == MF_VIDEO_FORMAT_MJPEG {
|
||||
if frame_rate_min != 0 {
|
||||
camera_format_list.push(MFCameraFormat {
|
||||
if frame_rate_min != 0 {
|
||||
camera_format_map.insert(
|
||||
MFCameraFormat {
|
||||
resolution: MFResolution {
|
||||
width_x: width,
|
||||
height_y: height,
|
||||
},
|
||||
format: MFFrameFormat::MJPEG,
|
||||
format: fourcc,
|
||||
frame_rate: frame_rate_min,
|
||||
});
|
||||
}
|
||||
|
||||
if frame_rate != 0 && frame_rate_min != frame_rate {
|
||||
camera_format_list.push(MFCameraFormat {
|
||||
resolution: MFResolution {
|
||||
width_x: width,
|
||||
height_y: height,
|
||||
},
|
||||
format: MFFrameFormat::MJPEG,
|
||||
frame_rate,
|
||||
});
|
||||
}
|
||||
|
||||
if frame_rate_max != 0 && frame_rate != frame_rate_max {
|
||||
camera_format_list.push(MFCameraFormat {
|
||||
resolution: MFResolution {
|
||||
width_x: width,
|
||||
height_y: height,
|
||||
},
|
||||
format: MFFrameFormat::MJPEG,
|
||||
frame_rate: frame_rate_max,
|
||||
});
|
||||
}
|
||||
} else if fourcc == MF_VIDEO_FORMAT_YUY2 {
|
||||
if frame_rate_min != 0 {
|
||||
camera_format_list.push(MFCameraFormat {
|
||||
resolution: MFResolution {
|
||||
width_x: width,
|
||||
height_y: height,
|
||||
},
|
||||
format: MFFrameFormat::YUYV,
|
||||
frame_rate: frame_rate_min,
|
||||
});
|
||||
}
|
||||
|
||||
if frame_rate != 0 && frame_rate_min != frame_rate {
|
||||
camera_format_list.push(MFCameraFormat {
|
||||
resolution: MFResolution {
|
||||
width_x: width,
|
||||
height_y: height,
|
||||
},
|
||||
format: MFFrameFormat::YUYV,
|
||||
frame_rate,
|
||||
});
|
||||
}
|
||||
|
||||
if frame_rate_max != 0 && frame_rate != frame_rate_max {
|
||||
camera_format_list.push(MFCameraFormat {
|
||||
resolution: MFResolution {
|
||||
width_x: width,
|
||||
height_y: height,
|
||||
},
|
||||
format: MFFrameFormat::YUYV,
|
||||
frame_rate: frame_rate_max,
|
||||
});
|
||||
}
|
||||
},
|
||||
media_type.clone(),
|
||||
);
|
||||
}
|
||||
|
||||
index += 1;
|
||||
if frame_rate != frame_rate_min && frame_rate != 0 {
|
||||
camera_format_map.insert(
|
||||
MFCameraFormat {
|
||||
resolution: MFResolution {
|
||||
width_x: width,
|
||||
height_y: height,
|
||||
},
|
||||
format: fourcc,
|
||||
frame_rate,
|
||||
},
|
||||
media_type.clone(),
|
||||
);
|
||||
}
|
||||
|
||||
if frame_rate_max != frame_rate
|
||||
&& frame_rate_max != frame_rate_min
|
||||
&& frame_rate_max != 0
|
||||
{
|
||||
camera_format_map.insert(
|
||||
MFCameraFormat {
|
||||
resolution: MFResolution {
|
||||
width_x: width,
|
||||
height_y: height,
|
||||
},
|
||||
format: fourcc,
|
||||
frame_rate: frame_rate_max,
|
||||
},
|
||||
media_type.clone(),
|
||||
);
|
||||
}
|
||||
}
|
||||
Ok(camera_format_list)
|
||||
Ok(camera_format_map)
|
||||
}
|
||||
|
||||
pub fn compatible_format_list(&mut self) -> Result<Vec<MFCameraFormat>, BindingError> {
|
||||
Ok(self
|
||||
.internal_format_list()?
|
||||
.into_keys()
|
||||
.into_iter()
|
||||
.collect())
|
||||
}
|
||||
|
||||
pub fn control(&self, control: MediaFoundationControls) -> Result<MFControl, BindingError> {
|
||||
@@ -1594,87 +1592,117 @@ pub mod wmf {
|
||||
}
|
||||
|
||||
pub fn set_format(&mut self, format: MFCameraFormat) -> Result<(), BindingError> {
|
||||
// convert to media_type
|
||||
let media_type = match unsafe { MFCreateMediaType() } {
|
||||
Ok(mt) => mt,
|
||||
Err(why) => return Err(BindingError::AttributeError(why.to_string())),
|
||||
};
|
||||
let mut index = 0;
|
||||
|
||||
// set relevant things
|
||||
let resolution = (u64::from(format.resolution.width_x) << 32_u64)
|
||||
+ u64::from(format.resolution.height_y);
|
||||
let fps = {
|
||||
let frame_rate_u64 = 0_u64;
|
||||
let mut bytes: [u8; 8] = frame_rate_u64.to_le_bytes();
|
||||
bytes[7] = format.frame_rate as u8;
|
||||
bytes[3] = 0x01;
|
||||
println!("{:?}", bytes);
|
||||
u64::from_le_bytes(bytes)
|
||||
};
|
||||
let fourcc = match format.format {
|
||||
MFFrameFormat::MJPEG => MF_VIDEO_FORMAT_MJPEG,
|
||||
MFFrameFormat::YUYV => MF_VIDEO_FORMAT_YUY2,
|
||||
};
|
||||
// setting to the new media_type
|
||||
if let Err(why) = unsafe { media_type.SetGUID(&MF_MT_MAJOR_TYPE, &MFMediaType_Video) } {
|
||||
return Err(BindingError::GUIDSetError(
|
||||
"MF_MT_MAJOR_TYPE".to_string(),
|
||||
"MFMediaType_Video".to_string(),
|
||||
why.to_string(),
|
||||
));
|
||||
}
|
||||
if let Err(why) = unsafe { media_type.SetGUID(&MF_MT_SUBTYPE, &fourcc) } {
|
||||
return Err(BindingError::GUIDSetError(
|
||||
"MF_MT_SUBTYPE".to_string(),
|
||||
format!("{:?}", fourcc),
|
||||
why.to_string(),
|
||||
));
|
||||
}
|
||||
if let Err(why) = unsafe { media_type.SetUINT64(&MF_MT_FRAME_SIZE, resolution) } {
|
||||
return Err(BindingError::GUIDSetError(
|
||||
"MF_MT_FRAME_SIZE".to_string(),
|
||||
resolution.to_string(),
|
||||
why.to_string(),
|
||||
));
|
||||
}
|
||||
if let Err(why) = unsafe { media_type.SetUINT64(&MF_MT_FRAME_RATE, fps) } {
|
||||
return Err(BindingError::GUIDSetError(
|
||||
"MF_MT_FRAME_RATE".to_string(),
|
||||
fps.to_string(),
|
||||
why.to_string(),
|
||||
));
|
||||
}
|
||||
if let Err(why) = unsafe { media_type.SetUINT64(&MF_MT_FRAME_RATE_RANGE_MIN, fps) } {
|
||||
return Err(BindingError::GUIDSetError(
|
||||
"MF_MT_FRAME_RATE_RANGE_MIN".to_string(),
|
||||
fps.to_string(),
|
||||
why.to_string(),
|
||||
));
|
||||
}
|
||||
if let Err(why) = unsafe { media_type.SetUINT64(&MF_MT_FRAME_RATE_RANGE_MAX, fps) } {
|
||||
return Err(BindingError::GUIDSetError(
|
||||
"MF_MT_FRAME_RATE_RANGE_MAX".to_string(),
|
||||
fps.to_string(),
|
||||
why.to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
let reserved = std::ptr::null_mut();
|
||||
if let Err(why) = unsafe {
|
||||
self.source_reader.SetCurrentMediaType(
|
||||
MEDIA_FOUNDATION_FIRST_VIDEO_STREAM,
|
||||
reserved,
|
||||
media_type.clone(),
|
||||
)
|
||||
while let Ok(media_type) = unsafe {
|
||||
self.source_reader
|
||||
.GetNativeMediaType(MEDIA_FOUNDATION_FIRST_VIDEO_STREAM, index)
|
||||
} {
|
||||
return Err(BindingError::GUIDSetError(
|
||||
"MF_SOURCE_READER_FIRST_VIDEO_STREAM".to_string(),
|
||||
format!("{:?}", media_type),
|
||||
why.to_string(),
|
||||
));
|
||||
index += 1;
|
||||
|
||||
let fourcc = match unsafe { media_type.GetGUID(&MF_MT_SUBTYPE) } {
|
||||
Ok(fcc) => match fcc {
|
||||
MF_VIDEO_FORMAT_YUY2 => MFFrameFormat::YUYV,
|
||||
MF_VIDEO_FORMAT_MJPEG => MFFrameFormat::MJPEG,
|
||||
_ => continue,
|
||||
},
|
||||
Err(why) => {
|
||||
return Err(BindingError::GUIDReadError(
|
||||
"MF_MT_SUBTYPE".to_string(),
|
||||
why.to_string(),
|
||||
))
|
||||
}
|
||||
};
|
||||
|
||||
let (width, height) = match unsafe { media_type.GetUINT64(&MF_MT_FRAME_SIZE) } {
|
||||
Ok(res_u64) => {
|
||||
let width = (res_u64 >> 32) as u32;
|
||||
let height = res_u64 as u32; // the cast will truncate the upper bits
|
||||
(width, height)
|
||||
}
|
||||
Err(why) => {
|
||||
return Err(BindingError::GUIDReadError(
|
||||
"MF_MT_FRAME_SIZE".to_string(),
|
||||
why.to_string(),
|
||||
))
|
||||
}
|
||||
};
|
||||
|
||||
// MFRatio is represented as 2 u32s in memory. This means we cann convert it to 2
|
||||
let frame_rate_max =
|
||||
match unsafe { media_type.GetUINT64(&MF_MT_FRAME_RATE_RANGE_MAX) } {
|
||||
Ok(fraction_u64) => {
|
||||
let mut numerator = (fraction_u64 >> 32) as u32;
|
||||
let denominator = fraction_u64 as u32;
|
||||
if denominator != 1 {
|
||||
numerator = 0;
|
||||
}
|
||||
numerator
|
||||
}
|
||||
Err(why) => {
|
||||
return Err(BindingError::GUIDReadError(
|
||||
"MF_MT_FRAME_RATE_RANGE_MAX".to_string(),
|
||||
why.to_string(),
|
||||
))
|
||||
}
|
||||
};
|
||||
|
||||
let frame_rate = match unsafe { media_type.GetUINT64(&MF_MT_FRAME_RATE) } {
|
||||
Ok(fraction_u64) => {
|
||||
let mut numerator = (fraction_u64 >> 32) as u32;
|
||||
let denominator = fraction_u64 as u32;
|
||||
if denominator != 1 {
|
||||
numerator = 0;
|
||||
}
|
||||
numerator
|
||||
}
|
||||
Err(why) => {
|
||||
return Err(BindingError::GUIDReadError(
|
||||
"MF_MT_FRAME_RATE".to_string(),
|
||||
why.to_string(),
|
||||
))
|
||||
}
|
||||
};
|
||||
|
||||
let frame_rate_min =
|
||||
match unsafe { media_type.GetUINT64(&MF_MT_FRAME_RATE_RANGE_MIN) } {
|
||||
Ok(fraction_u64) => {
|
||||
let mut numerator = (fraction_u64 >> 32) as u32;
|
||||
let denominator = fraction_u64 as u32;
|
||||
if denominator != 1 {
|
||||
numerator = 0;
|
||||
}
|
||||
numerator
|
||||
}
|
||||
Err(why) => {
|
||||
return Err(BindingError::GUIDReadError(
|
||||
"MF_MT_FRAME_RATE_RANGE_MIN".to_string(),
|
||||
why.to_string(),
|
||||
))
|
||||
}
|
||||
};
|
||||
|
||||
if format.width() == width
|
||||
&& format.height() == height
|
||||
&& [frame_rate_max, frame_rate, frame_rate_min].contains(&format.frame_rate)
|
||||
&& format.format == fourcc
|
||||
{
|
||||
return match unsafe {
|
||||
self.source_reader.SetCurrentMediaType(
|
||||
MEDIA_FOUNDATION_FIRST_VIDEO_STREAM,
|
||||
null_mut(),
|
||||
media_type,
|
||||
)
|
||||
} {
|
||||
Ok(_) => {
|
||||
self.device_format = format;
|
||||
Ok(())
|
||||
}
|
||||
Err(why) => Err(BindingError::AttributeError(why.to_string())),
|
||||
};
|
||||
}
|
||||
}
|
||||
self.device_format = format;
|
||||
Ok(())
|
||||
Err(BindingError::EnumerateError("Not Found".to_string()))
|
||||
}
|
||||
|
||||
pub fn is_stream_open(&self) -> bool {
|
||||
@@ -1686,7 +1714,6 @@ pub mod wmf {
|
||||
self.source_reader
|
||||
.SetStreamSelection(MEDIA_FOUNDATION_FIRST_VIDEO_STREAM, true)
|
||||
} {
|
||||
println!("a");
|
||||
return Err(BindingError::ReadFrameError(why.to_string()));
|
||||
}
|
||||
|
||||
|
||||
@@ -47,10 +47,8 @@ impl<'a> MediaFoundationCaptureDevice<'a> {
|
||||
/// # Errors
|
||||
/// This function will error if Media Foundation fails to get the device.
|
||||
pub fn new(index: usize, camera_fmt: Option<CameraFormat>) -> Result<Self, NokhwaError> {
|
||||
let mut mf_device = MediaFoundationDevice::new(index)?;
|
||||
if let Some(fmt) = camera_fmt {
|
||||
mf_device.set_format(fmt.into())?;
|
||||
}
|
||||
let format = camera_fmt.unwrap_or_default();
|
||||
let mut mf_device = MediaFoundationDevice::new(index, format.into())?;
|
||||
|
||||
let info = CameraInfo::new(
|
||||
mf_device.name(),
|
||||
|
||||
Reference in New Issue
Block a user