diff --git a/Cargo.toml b/Cargo.toml index 27cc9b5..ee54b4c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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] diff --git a/examples/capture/src/main.rs b/examples/capture/src/main.rs index 904b573..3a3f7d6 100644 --- a/examples/capture/src/main.rs +++ b/examples/capture/src/main.rs @@ -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)] diff --git a/nokhwa-bindings-windows/Cargo.toml b/nokhwa-bindings-windows/Cargo.toml index aa1ab43..be44295 100644 --- a/nokhwa-bindings-windows/Cargo.toml +++ b/nokhwa-bindings-windows/Cargo.toml @@ -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" diff --git a/nokhwa-bindings-windows/src/lib.rs b/nokhwa-bindings-windows/src/lib.rs index 81d4c14..2862781 100644 --- a/nokhwa-bindings-windows/src/lib.rs +++ b/nokhwa-bindings-windows/src/lib.rs @@ -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 { + fn internal(index: usize) -> Result { 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 { + let mut camera = MediaFoundationDevice::internal(index)?; + camera.set_format(format)?; + Ok(camera) + } + pub fn with_string(unique_id: &[u16]) -> Result { - 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, BindingError> { - let mut camera_format_list = vec![]; + fn internal_format_list( + &mut self, + ) -> Result, 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, BindingError> { + Ok(self + .internal_format_list()? + .into_keys() + .into_iter() + .collect()) } pub fn control(&self, control: MediaFoundationControls) -> Result { @@ -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())); } diff --git a/src/backends/capture/msmf.rs b/src/backends/capture/msmf.rs index 2e6f92f..3f29cf1 100644 --- a/src/backends/capture/msmf.rs +++ b/src/backends/capture/msmf.rs @@ -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) -> Result { - 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(),