1#![deny(clippy::pedantic)]
18#![warn(clippy::all)]
19#![allow(clippy::missing_errors_doc)]
20#![allow(clippy::cast_possible_truncation)]
21#![allow(clippy::too_many_lines)]
22
23#[cfg(all(windows, not(feature = "docs-only")))]
31pub mod wmf {
32 use nokhwa_core::error::NokhwaError;
33 use nokhwa_core::types::{
34 ApiBackend, CameraControl, CameraFormat, CameraIndex, CameraInfo, ControlValueDescription,
35 ControlValueSetter, FrameFormat, KnownCameraControl, KnownCameraControlFlag, Resolution,
36 };
37 use once_cell::sync::Lazy;
38 use std::ffi::c_void;
39 use std::{
40 borrow::Cow,
41 cell::Cell,
42 mem::MaybeUninit,
43 slice::from_raw_parts,
44 sync::{
45 atomic::{AtomicBool, AtomicUsize, Ordering},
46 Arc,
47 },
48 };
49 use windows::Win32::Media::DirectShow::{CameraControl_Flags_Auto, CameraControl_Flags_Manual};
50 use windows::Win32::Media::MediaFoundation::{
51 MFCreateSample, MF_SOURCE_READER_FIRST_VIDEO_STREAM,
52 };
53 use windows::{
54 core::{Interface, GUID, PWSTR},
55 Win32::{
56 Media::{
57 DirectShow::{
58 CameraControl_Exposure, CameraControl_Focus, CameraControl_Iris,
59 CameraControl_Pan, CameraControl_Tilt, CameraControl_Zoom, IAMCameraControl,
60 IAMVideoProcAmp, VideoProcAmp_BacklightCompensation, VideoProcAmp_Brightness,
61 VideoProcAmp_ColorEnable, VideoProcAmp_Contrast, VideoProcAmp_Gain,
62 VideoProcAmp_Gamma, VideoProcAmp_Hue, VideoProcAmp_Saturation,
63 VideoProcAmp_Sharpness, VideoProcAmp_WhiteBalance,
64 },
65 KernelStreaming::GUID_NULL,
66 MediaFoundation::{
67 IMFActivate, IMFAttributes, IMFMediaSource, IMFSample, IMFSourceReader,
68 MFCreateAttributes, MFCreateSourceReaderFromMediaSource, MFEnumDeviceSources,
69 MFShutdown, MFStartup, MFSTARTUP_NOSOCKET, MF_API_VERSION,
70 MF_DEVSOURCE_ATTRIBUTE_FRIENDLY_NAME, MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE,
71 MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID,
72 MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK, MF_MT_FRAME_RATE,
73 MF_MT_FRAME_RATE_RANGE_MAX, MF_MT_FRAME_RATE_RANGE_MIN, MF_MT_FRAME_SIZE,
74 MF_MT_SUBTYPE, MF_READWRITE_DISABLE_CONVERTERS,
75 },
76 },
77 System::Com::{CoInitializeEx, CoUninitialize, COINIT},
78 },
79 };
80
81 static INITIALIZED: Lazy<Arc<AtomicBool>> = Lazy::new(|| Arc::new(AtomicBool::new(false)));
82 static CAMERA_REFCNT: Lazy<Arc<AtomicUsize>> = Lazy::new(|| Arc::new(AtomicUsize::new(0)));
83
84 const CO_INIT_APARTMENT_THREADED: COINIT = COINIT(0x2);
86 const CO_INIT_DISABLE_OLE1DDE: COINIT = COINIT(0x4);
87
88 const MF_VIDEO_FORMAT_YUY2: GUID = GUID::from_values(
90 0x3259_5559,
91 0x0000,
92 0x0010,
93 [0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71],
94 );
95 const MF_VIDEO_FORMAT_MJPEG: GUID = GUID::from_values(
96 0x4750_4A4D,
97 0x0000,
98 0x0010,
99 [0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71],
100 );
101 const MF_VIDEO_FORMAT_GRAY: GUID = GUID::from_values(
102 0x3030_3859,
103 0x0000,
104 0x0010,
105 [0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71],
106 );
107 const MF_VIDEO_FORMAT_NV12: GUID = GUID::from_values(
108 0x3231_564E,
109 0x0000,
110 0x0010,
111 [0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71],
112 );
113 const MF_VIDEO_FORMAT_RGB24: GUID = GUID::from_values(
114 0x0000_0014,
115 0x0000,
116 0x0010,
117 [0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71],
118 );
119
120 const MEDIA_FOUNDATION_FIRST_VIDEO_STREAM: u32 = 0xFFFF_FFFC;
121 const MF_SOURCE_READER_MEDIASOURCE: u32 = 0xFFFF_FFFF;
122
123 fn guid_to_frameformat(guid: GUID) -> Option<FrameFormat> {
165 match guid {
166 MF_VIDEO_FORMAT_NV12 => Some(FrameFormat::NV12),
167 MF_VIDEO_FORMAT_RGB24 => Some(FrameFormat::RAWBGR),
168 MF_VIDEO_FORMAT_GRAY => Some(FrameFormat::GRAY),
169 MF_VIDEO_FORMAT_YUY2 => Some(FrameFormat::YUYV),
170 MF_VIDEO_FORMAT_MJPEG => Some(FrameFormat::MJPEG),
171 _ => None,
172 }
173 }
174
175 pub fn initialize_mf() -> Result<(), NokhwaError> {
176 if !(INITIALIZED.load(Ordering::SeqCst)) {
177 if let Err(why) = unsafe {
178 CoInitializeEx(None, CO_INIT_APARTMENT_THREADED | CO_INIT_DISABLE_OLE1DDE).ok()
179 } {
180 return Err(NokhwaError::InitializeError {
181 backend: ApiBackend::MediaFoundation,
182 error: why.to_string(),
183 });
184 }
185
186 if let Err(why) = unsafe { MFStartup(MF_API_VERSION, MFSTARTUP_NOSOCKET) } {
187 unsafe {
188 CoUninitialize();
189 }
190 return Err(NokhwaError::InitializeError {
191 backend: ApiBackend::MediaFoundation,
192 error: why.to_string(),
193 });
194 }
195 INITIALIZED.store(true, Ordering::SeqCst);
196 }
197 Ok(())
198 }
199
200 pub fn de_initialize_mf() -> Result<(), NokhwaError> {
201 if INITIALIZED.load(Ordering::SeqCst) {
202 unsafe {
203 if let Err(why) = MFShutdown() {
204 return Err(NokhwaError::ShutdownError {
205 backend: ApiBackend::MediaFoundation,
206 error: why.to_string(),
207 });
208 }
209 CoUninitialize();
210 INITIALIZED.store(false, Ordering::SeqCst);
211 }
212 }
213 Ok(())
214 }
215
216 fn query_activate_pointers() -> Result<Vec<IMFActivate>, NokhwaError> {
217 initialize_mf()?;
218
219 let mut attributes: Option<IMFAttributes> = None;
220 if let Err(why) = unsafe { MFCreateAttributes(&mut attributes, 1) } {
221 return Err(NokhwaError::GetPropertyError {
222 property: "IMFAttributes".to_string(),
223 error: why.to_string(),
224 });
225 }
226
227 let attributes = match attributes {
228 Some(attr) => {
229 if let Err(why) = unsafe {
230 attr.SetGUID(
231 &MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE,
232 &MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID,
233 )
234 } {
235 return Err(NokhwaError::SetPropertyError {
236 property: "GUID MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE".to_string(),
237 value: "MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID".to_string(),
238 error: why.to_string(),
239 });
240 }
241 attr
242 }
243 None => {
244 return Err(NokhwaError::SetPropertyError {
245 property: "GUID MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE".to_string(),
246 value: "MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID".to_string(),
247 error: "Call to IMFAttributes::SetGUID failed - IMFAttributes is None"
248 .to_string(),
249 });
250 }
251 };
252
253 let mut count: u32 = 0;
254 let mut unused_mf_activate: MaybeUninit<*mut Option<IMFActivate>> = MaybeUninit::uninit();
255
256 if let Err(why) =
257 unsafe { MFEnumDeviceSources(&attributes, unused_mf_activate.as_mut_ptr(), &mut count) }
258 {
259 return Err(NokhwaError::StructureError {
260 structure: "MFEnumDeviceSources".to_string(),
261 error: why.to_string(),
262 });
263 }
264
265 let mut device_list = vec![];
266 if count == 0 {
267 return Ok(device_list);
268 }
269
270 unsafe { from_raw_parts(unused_mf_activate.assume_init(), count as usize) }
271 .iter()
272 .for_each(|pointer| {
273 if let Some(imf_activate) = pointer {
274 device_list.push(imf_activate.clone());
275 }
276 });
277
278 Ok(device_list)
279 }
280
281 fn activate_to_descriptors(
282 index: CameraIndex,
283 imf_activate: &IMFActivate,
284 ) -> Result<CameraInfo, NokhwaError> {
285 let mut pwstr_name = PWSTR(&mut 0_u16);
286 let mut len_pwstrname = 0;
287 let mut pwstr_symlink = PWSTR(&mut 0_u16);
288 let mut len_pwstrsymlink = 0;
289
290 if let Err(why) = unsafe {
291 imf_activate.GetAllocatedString(
292 &MF_DEVSOURCE_ATTRIBUTE_FRIENDLY_NAME,
293 &mut pwstr_name,
294 &mut len_pwstrname,
295 )
296 } {
297 return Err(NokhwaError::GetPropertyError {
298 property: "MF_DEVSOURCE_ATTRIBUTE_FRIENDLY_NAME".to_string(),
299 error: why.to_string(),
300 });
301 }
302
303 if let Err(why) = unsafe {
304 imf_activate.GetAllocatedString(
305 &MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK,
306 &mut pwstr_symlink,
307 &mut len_pwstrsymlink,
308 )
309 } {
310 return Err(NokhwaError::GetPropertyError {
311 property: "MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK".to_string(),
312 error: why.to_string(),
313 });
314 }
315
316 if pwstr_name.is_null() {
317 return Err(NokhwaError::GetPropertyError {
318 property: "MF_DEVSOURCE_ATTRIBUTE_FRIENDLY_NAME".to_string(),
319 error: "Call to IMFActivate::GetAllocatedString failed - PWSTR is null".to_string(),
320 });
321 }
322 if pwstr_symlink.is_null() {
323 return Err(NokhwaError::GetPropertyError {
324 property: "MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK".to_string(),
325 error: "Call to IMFActivate::GetAllocatedString failed - PWSTR is null".to_string(),
326 });
327 }
328
329 let name = unsafe {
330 pwstr_name
331 .to_string()
332 .map_err(|x| NokhwaError::StructureError {
333 structure: "PWSTR/String - Name".to_string(),
334 error: x.to_string(),
335 })?
336 };
337 let symlink = unsafe {
338 pwstr_symlink
339 .to_string()
340 .map_err(|x| NokhwaError::StructureError {
341 structure: "PWSTR/String - Symlink".to_string(),
342 error: x.to_string(),
343 })?
344 };
345
346 Ok(CameraInfo::new(
347 &name,
348 "MediaFoundation Camera",
349 &symlink,
350 index,
351 ))
352 }
353
354 pub fn query_media_foundation_descriptors() -> Result<Vec<CameraInfo>, NokhwaError> {
355 let mut device_list = vec![];
356
357 for (index, activate_ptr) in query_activate_pointers()?.into_iter().enumerate() {
358 device_list.push(activate_to_descriptors(
359 CameraIndex::Index(index as u32),
360 &activate_ptr,
361 )?);
362 }
363 Ok(device_list)
364 }
365
366 #[derive(Copy, Clone, Debug, PartialOrd, PartialEq, Eq)]
367 enum MFControlId {
368 ProcAmpBoolean(i32),
369 ProcAmpRange(i32),
370 CCValue(i32),
371 CCRange(i32),
372 }
373
374 #[allow(clippy::cast_sign_loss)]
375 fn kcc_to_i32(kcc: KnownCameraControl) -> Option<MFControlId> {
376 let control_id = match kcc {
377 KnownCameraControl::Brightness => MFControlId::ProcAmpRange(VideoProcAmp_Brightness.0),
378 KnownCameraControl::Contrast => MFControlId::ProcAmpRange(VideoProcAmp_Contrast.0),
379 KnownCameraControl::Hue => MFControlId::ProcAmpRange(VideoProcAmp_Hue.0),
380 KnownCameraControl::Saturation => MFControlId::ProcAmpRange(VideoProcAmp_Saturation.0),
381 KnownCameraControl::Sharpness => MFControlId::ProcAmpRange(VideoProcAmp_Sharpness.0),
382 KnownCameraControl::Gamma => MFControlId::ProcAmpRange(VideoProcAmp_Gamma.0),
383 KnownCameraControl::WhiteBalance => {
384 MFControlId::ProcAmpRange(VideoProcAmp_WhiteBalance.0)
385 }
386 KnownCameraControl::BacklightComp => {
387 MFControlId::ProcAmpBoolean(VideoProcAmp_BacklightCompensation.0)
388 }
389 KnownCameraControl::Gain => MFControlId::ProcAmpRange(VideoProcAmp_Gain.0),
390 KnownCameraControl::Pan => MFControlId::CCRange(CameraControl_Pan.0),
391 KnownCameraControl::Tilt => MFControlId::CCRange(CameraControl_Tilt.0),
392 KnownCameraControl::Zoom => MFControlId::CCRange(CameraControl_Zoom.0),
393 KnownCameraControl::Exposure => MFControlId::CCValue(CameraControl_Exposure.0),
394 KnownCameraControl::Iris => MFControlId::CCValue(CameraControl_Iris.0),
395 KnownCameraControl::Focus => MFControlId::CCValue(CameraControl_Focus.0),
396 KnownCameraControl::Other(o) => {
397 if o == VideoProcAmp_ColorEnable.0 as u128 {
398 MFControlId::ProcAmpRange(o as i32)
399 } else {
400 return None;
401 }
402 }
403 };
404
405 Some(control_id)
406 }
407
408 pub struct MediaFoundationDevice {
409 is_open: Cell<bool>,
410 device_specifier: CameraInfo,
411 device_format: CameraFormat,
412 source_reader: IMFSourceReader,
413 }
414
415 impl MediaFoundationDevice {
416 pub fn new(index: CameraIndex) -> Result<Self, NokhwaError> {
417 initialize_mf()?;
418 match index {
419 CameraIndex::Index(i) => {
420 let (media_source, device_descriptor) =
421 match query_activate_pointers()?.into_iter().nth(i as usize) {
422 Some(activate) => {
423 match unsafe { activate.ActivateObject::<IMFMediaSource>() } {
424 Ok(media_source) => {
425 (media_source, activate_to_descriptors(index, &activate)?)
426 }
427 Err(why) => {
428 return Err(NokhwaError::OpenDeviceError(
429 index.to_string(),
430 why.to_string(),
431 ))
432 }
433 }
434 }
435 None => {
436 return Err(NokhwaError::OpenDeviceError(
437 index.to_string(),
438 "No device".to_string(),
439 ))
440 }
441 };
442
443 let source_reader_attr = {
444 let attr = match {
445 let mut attr: Option<IMFAttributes> = None;
446
447 if let Err(why) = unsafe { MFCreateAttributes(&mut attr, 3) } {
448 return Err(NokhwaError::StructureError {
449 structure: "MFCreateAttributes".to_string(),
450 error: why.to_string(),
451 });
452 }
453 attr
454 } {
455 Some(imf_attr) => imf_attr,
456 None => {
457 return Err(NokhwaError::StructureError {
458 structure: "MFCreateAttributes".to_string(),
459 error: "Attributee Alloc Failure".to_string(),
460 });
461 }
462 };
463
464 if let Err(why) = unsafe {
465 attr.SetUINT32(&MF_READWRITE_DISABLE_CONVERTERS, u32::from(true))
466 } {
467 return Err(NokhwaError::SetPropertyError {
468 property: "MF_READWRITE_DISABLE_CONVERTERS".to_string(),
469 value: u32::from(true).to_string(),
470 error: why.to_string(),
471 });
472 }
473
474 attr
475 };
476
477 let source_reader = match unsafe {
478 MFCreateSourceReaderFromMediaSource(&media_source, &source_reader_attr)
479 } {
480 Ok(sr) => sr,
481 Err(why) => {
482 return Err(NokhwaError::StructureError {
483 structure: "MFCreateSourceReaderFromMediaSource".to_string(),
484 error: why.to_string(),
485 })
486 }
487 };
488
489 CAMERA_REFCNT.store(CAMERA_REFCNT.load(Ordering::SeqCst) + 1, Ordering::SeqCst);
491
492 Ok(MediaFoundationDevice {
493 is_open: Cell::new(false),
494 device_specifier: device_descriptor,
495 device_format: CameraFormat::default(),
496 source_reader,
497 })
498 }
499 CameraIndex::String(s) => {
500 let devicelist = query_media_foundation_descriptors()?;
501 let mut id_eq = None;
502
503 for mfdev in devicelist {
504 if mfdev.misc() == s {
505 id_eq = Some(mfdev.index().as_index()?);
506 break;
507 }
508 }
509
510 match id_eq {
511 Some(index) => Self::new(CameraIndex::Index(index)),
512 None => Err(NokhwaError::OpenDeviceError(s, "Not Found".to_string())),
513 }
514 }
515 }
516 }
517 pub fn index(&self) -> &CameraIndex {
545 self.device_specifier.index()
546 }
547
548 pub fn name(&self) -> String {
549 self.device_specifier.human_name()
550 }
551
552 pub fn symlink(&self) -> String {
553 self.device_specifier.misc()
554 }
555
556 pub fn compatible_format_list(&mut self) -> Result<Vec<CameraFormat>, NokhwaError> {
557 let mut camera_format_list = vec![];
558 let mut index = 0;
559
560 while let Ok(media_type) = unsafe {
561 self.source_reader
562 .GetNativeMediaType(MEDIA_FOUNDATION_FIRST_VIDEO_STREAM, index)
563 } {
564 index += 1;
565 let fourcc = match unsafe { media_type.GetGUID(&MF_MT_SUBTYPE) } {
566 Ok(fcc) => fcc,
567 Err(why) => {
568 return Err(NokhwaError::GetPropertyError {
569 property: "MF_MT_SUBTYPE".to_string(),
570 error: why.to_string(),
571 })
572 }
573 };
574
575 let (width, height) = match unsafe { media_type.GetUINT64(&MF_MT_FRAME_SIZE) } {
576 Ok(res_u64) => {
577 let width = (res_u64 >> 32) as u32;
578 let height = res_u64 as u32; (width, height)
580 }
581 Err(why) => {
582 return Err(NokhwaError::GetPropertyError {
583 property: "MF_MT_FRAME_SIZE".to_string(),
584 error: why.to_string(),
585 })
586 }
587 };
588
589 let framerate_list = {
591 let mut framerates = vec![0_u32; 3];
592 if let Ok(fraction_u64) =
593 unsafe { media_type.GetUINT64(&MF_MT_FRAME_RATE_RANGE_MAX) }
594 {
595 let mut numerator = (fraction_u64 >> 32) as u32;
596 let denominator = fraction_u64 as u32;
597 if denominator != 1 {
598 numerator = 0;
599 }
600 framerates.push(numerator);
601 };
602 if let Ok(fraction_u64) = unsafe { media_type.GetUINT64(&MF_MT_FRAME_RATE) } {
603 let mut numerator = (fraction_u64 >> 32) as u32;
604 let denominator = fraction_u64 as u32;
605 if denominator != 1 {
606 numerator = 0;
607 }
608 framerates.push(numerator);
609 };
610 if let Ok(fraction_u64) =
611 unsafe { media_type.GetUINT64(&MF_MT_FRAME_RATE_RANGE_MIN) }
612 {
613 let mut numerator = (fraction_u64 >> 32) as u32;
614 let denominator = fraction_u64 as u32;
615 if denominator != 1 {
616 numerator = 0;
617 }
618 framerates.push(numerator);
619 };
620 framerates
621 };
622
623 let frame_fmt = match guid_to_frameformat(fourcc) {
624 Some(fcc) => fcc,
625 None => continue,
626 };
627
628 for frame_rate in framerate_list {
629 if frame_rate != 0 {
630 camera_format_list.push(CameraFormat::new(
631 Resolution::new(width, height),
632 frame_fmt,
633 frame_rate,
634 ));
635 }
636 }
637 }
638 Ok(camera_format_list)
639 }
640
641 pub fn control(&self, control: KnownCameraControl) -> Result<CameraControl, NokhwaError> {
642 let camera_control = unsafe {
643 let mut receiver: MaybeUninit<IAMCameraControl> = MaybeUninit::uninit();
644 let ptr_receiver = receiver.as_mut_ptr();
645 if let Err(why) = self.source_reader.GetServiceForStream(
646 MF_SOURCE_READER_MEDIASOURCE,
647 &GUID_NULL,
648 &IAMCameraControl::IID,
649 ptr_receiver
650 .cast::<IAMCameraControl>()
651 .cast::<*mut c_void>(),
652 ) {
653 return Err(NokhwaError::SetPropertyError {
654 property: "MF_SOURCE_READER_MEDIASOURCE".to_string(),
655 value: "IAMCameraControl".to_string(),
656 error: why.to_string(),
657 });
658 }
659 receiver.assume_init()
660 };
661 let video_proc_amp = unsafe {
662 let mut receiver: MaybeUninit<IAMVideoProcAmp> = MaybeUninit::uninit();
663 let ptr_receiver = receiver.as_mut_ptr();
664 if let Err(why) = self.source_reader.GetServiceForStream(
665 MF_SOURCE_READER_MEDIASOURCE,
666 &GUID_NULL,
667 &IAMVideoProcAmp::IID,
668 ptr_receiver.cast::<IAMVideoProcAmp>().cast::<*mut c_void>(),
669 ) {
670 return Err(NokhwaError::SetPropertyError {
671 property: "MF_SOURCE_READER_MEDIASOURCE".to_string(),
672 value: "IAMVideoProcAmp".to_string(),
673 error: why.to_string(),
674 });
675 }
676 receiver.assume_init()
677 };
678
679 let mut min = 0;
680 let mut max = 0;
681 let mut step = 0;
682 let mut default = 0;
683 let mut value = 0;
684 let mut flag = 0;
685
686 let control_id = kcc_to_i32(control).ok_or(NokhwaError::SetPropertyError {
687 property: "CameraControl".to_string(),
688 value: control.to_string(),
689 error: "Does not exist".to_string(),
690 })?;
691
692 let ctrl_value_set = match control_id {
693 MFControlId::ProcAmpBoolean(id) => unsafe {
694 if let Err(why) = video_proc_amp.GetRange(
695 id,
696 &mut min,
697 &mut max,
698 &mut step,
699 &mut default,
700 &mut flag,
701 ) {
702 return Err(NokhwaError::GetPropertyError {
703 property: format!("{:?}: {} - Range", control_id, control),
704 error: why.to_string(),
705 });
706 }
707 if let Err(why) = video_proc_amp.Get(id, &mut value, &mut flag) {
708 return Err(NokhwaError::GetPropertyError {
709 property: format!("{:?}: {} - Value", control_id, control),
710 error: why.to_string(),
711 });
712 }
713
714 let boolval = value != 0;
715 let booldef = default != 0;
716 ControlValueDescription::Boolean {
717 value: boolval,
718 default: booldef,
719 }
720 },
721 MFControlId::ProcAmpRange(id) => unsafe {
722 if let Err(why) = video_proc_amp.GetRange(
723 id,
724 &mut min,
725 &mut max,
726 &mut step,
727 &mut default,
728 &mut flag,
729 ) {
730 return Err(NokhwaError::GetPropertyError {
731 property: format!("{:?}: {} - Range", control_id, control),
732 error: why.to_string(),
733 });
734 }
735 if let Err(why) = video_proc_amp.Get(id, &mut value, &mut flag) {
736 return Err(NokhwaError::GetPropertyError {
737 property: format!("{:?}: {} - Value", control_id, control),
738 error: why.to_string(),
739 });
740 }
741 ControlValueDescription::IntegerRange {
742 min: i64::from(min),
743 max: i64::from(max),
744 value: i64::from(value),
745 step: i64::from(step),
746 default: i64::from(default),
747 }
748 },
749 MFControlId::CCValue(id) => unsafe {
750 if let Err(why) = camera_control.GetRange(
751 id,
752 &mut min,
753 &mut max,
754 &mut step,
755 &mut default,
756 &mut flag,
757 ) {
758 return Err(NokhwaError::GetPropertyError {
759 property: format!("{:?}: {} - Range", control_id, control),
760 error: why.to_string(),
761 });
762 }
763 if let Err(why) = camera_control.Get(id, &mut value, &mut flag) {
764 return Err(NokhwaError::GetPropertyError {
765 property: format!("{:?}: {} - Value", control_id, control),
766 error: why.to_string(),
767 });
768 }
769
770 ControlValueDescription::Integer {
771 value: i64::from(value),
772 default: i64::from(default),
773 step: i64::from(step),
774 }
775 },
776 MFControlId::CCRange(id) => unsafe {
777 if let Err(why) = camera_control.GetRange(
778 id,
779 &mut min,
780 &mut max,
781 &mut step,
782 &mut default,
783 &mut flag,
784 ) {
785 return Err(NokhwaError::GetPropertyError {
786 property: format!("{:?}: {} - Range", control_id, control),
787 error: why.to_string(),
788 });
789 }
790 if let Err(why) = camera_control.Get(id, &mut value, &mut flag) {
791 return Err(NokhwaError::GetPropertyError {
792 property: format!("{:?}: {} - Value", control_id, control),
793 error: why.to_string(),
794 });
795 }
796 ControlValueDescription::IntegerRange {
797 min: i64::from(min),
798 max: i64::from(max),
799 value: i64::from(value),
800 step: i64::from(step),
801 default: i64::from(default),
802 }
803 },
804 };
805
806 let is_manual = if flag == CameraControl_Flags_Manual.0 {
807 KnownCameraControlFlag::Manual
808 } else {
809 KnownCameraControlFlag::Automatic
810 };
811
812 Ok(CameraControl::new(
813 control,
814 control.to_string(),
815 ctrl_value_set,
816 vec![is_manual],
817 true,
818 ))
819 }
820
821 pub fn set_control(
822 &mut self,
823 control: KnownCameraControl,
824 value: ControlValueSetter,
825 ) -> Result<(), NokhwaError> {
826 let current_value = self.control(control)?;
827
828 let camera_control = unsafe {
829 let mut receiver: MaybeUninit<IAMCameraControl> = MaybeUninit::uninit();
830 let ptr_receiver = receiver.as_mut_ptr();
831 if let Err(why) = self.source_reader.GetServiceForStream(
832 MF_SOURCE_READER_MEDIASOURCE,
833 &GUID_NULL,
834 &IAMCameraControl::IID,
835 ptr_receiver
836 .cast::<IAMCameraControl>()
837 .cast::<*mut c_void>(),
838 ) {
839 return Err(NokhwaError::SetPropertyError {
840 property: "MF_SOURCE_READER_MEDIASOURCE".to_string(),
841 value: "IAMCameraControl".to_string(),
842 error: why.to_string(),
843 });
844 }
845 receiver.assume_init()
846 };
847 let video_proc_amp = unsafe {
848 let mut receiver: MaybeUninit<IAMVideoProcAmp> = MaybeUninit::uninit();
849 let ptr_receiver = receiver.as_mut_ptr();
850 if let Err(why) = self.source_reader.GetServiceForStream(
851 MF_SOURCE_READER_MEDIASOURCE,
852 &GUID_NULL,
853 &IAMVideoProcAmp::IID,
854 ptr_receiver.cast::<IAMVideoProcAmp>().cast::<*mut c_void>(),
855 ) {
856 return Err(NokhwaError::SetPropertyError {
857 property: "MF_SOURCE_READER_MEDIASOURCE".to_string(),
858 value: "IAMVideoProcAmp".to_string(),
859 error: why.to_string(),
860 });
861 }
862 receiver.assume_init()
863 };
864
865 let control_id = kcc_to_i32(control).ok_or(NokhwaError::SetPropertyError {
866 property: "CameraControl".to_string(),
867 value: control.to_string(),
868 error: "Does not exist".to_string(),
869 })?;
870
871 let ctrl_value = match value {
872 ControlValueSetter::Integer(i) => i as i32,
873 ControlValueSetter::Boolean(b) => i32::from(b),
874 v => {
875 return Err(NokhwaError::StructureError {
876 structure: format!("ControlValueSetter {}", v),
877 error: "invalid value type".to_string(),
878 })
879 }
880 };
881
882 let flag = current_value
883 .flag()
884 .get(0)
885 .map(|x| {
886 if *x == KnownCameraControlFlag::Automatic {
887 CameraControl_Flags_Auto
888 } else {
889 CameraControl_Flags_Manual
890 }
891 })
892 .ok_or(NokhwaError::StructureError {
893 structure: "KnownCameraControlFlag".to_string(),
894 error: "could not cast to i32".to_string(),
895 })?;
896
897 match control_id {
898 MFControlId::ProcAmpBoolean(id) | MFControlId::ProcAmpRange(id) => unsafe {
899 if let Err(why) = video_proc_amp.Set(id, ctrl_value, flag.0) {
900 return Err(NokhwaError::SetPropertyError {
901 property: control.to_string(),
902 value: ctrl_value.to_string(),
903 error: why.to_string(),
904 });
905 }
906 },
907 MFControlId::CCValue(id) | MFControlId::CCRange(id) => unsafe {
908 if let Err(why) = camera_control.Set(id, ctrl_value, flag.0) {
909 return Err(NokhwaError::SetPropertyError {
910 property: control.to_string(),
911 value: ctrl_value.to_string(),
912 error: why.to_string(),
913 });
914 }
915 },
916 }
917
918 Ok(())
919 }
920
921 #[allow(clippy::cast_sign_loss)]
922 pub fn format_refreshed(&mut self) -> Result<CameraFormat, NokhwaError> {
923 match unsafe {
924 self.source_reader
925 .GetCurrentMediaType(MF_SOURCE_READER_FIRST_VIDEO_STREAM.0 as u32)
926 } {
927 Ok(media_type) => {
928 let resolution = match unsafe { media_type.GetUINT64(&MF_MT_FRAME_SIZE) } {
929 Ok(res) => {
930 let width = (res >> 32) as u32;
931 let height = ((res << 32) >> 32) as u32;
932
933 Resolution {
934 width_x: width,
935 height_y: height,
936 }
937 }
938 Err(why) => {
939 return Err(NokhwaError::GetPropertyError {
940 property: "MF_MT_FRAME_SIZE".to_string(),
941 error: why.to_string(),
942 })
943 }
944 };
945
946 let frame_rate = match unsafe { media_type.GetUINT64(&MF_MT_FRAME_RATE) } {
947 Ok(fps) => fps as u32,
948 Err(why) => {
949 return Err(NokhwaError::GetPropertyError {
950 property: "MF_MT_FRAME_RATE".to_string(),
951 error: why.to_string(),
952 })
953 }
954 };
955
956 let format = match unsafe { media_type.GetGUID(&MF_MT_SUBTYPE) } {
957 Ok(fcc) => match guid_to_frameformat(fcc) {
958 Some(ff) => ff,
959 None => {
960 return Err(NokhwaError::GetPropertyError {
961 property: "MF_MT_SUBTYPE".to_string(),
962 error: "Unknown".to_string(),
963 })
964 }
965 },
966 Err(why) => {
967 return Err(NokhwaError::GetPropertyError {
968 property: "MF_MT_SUBTYPE".to_string(),
969 error: why.to_string(),
970 })
971 }
972 };
973
974 let cfmt = CameraFormat::new(resolution, format, frame_rate);
975 self.device_format = cfmt;
976
977 Ok(cfmt)
978 }
979 Err(why) => Err(NokhwaError::GetPropertyError {
980 property: "MF_SOURCE_READER_FIRST_VIDEO_STREAM".to_string(),
981 error: why.to_string(),
982 }),
983 }
984 }
985
986 pub fn format(&self) -> CameraFormat {
987 self.device_format
988 }
989
990 pub fn set_format(&mut self, format: CameraFormat) -> Result<(), NokhwaError> {
991 let mut last_error: Option<NokhwaError> = None;
996
997 let mut index = 0;
998 while let Ok(media_type) = unsafe {
999 self.source_reader
1000 .GetNativeMediaType(MEDIA_FOUNDATION_FIRST_VIDEO_STREAM, index)
1001 } {
1002 index += 1;
1003 let fourcc = match unsafe { media_type.GetGUID(&MF_MT_SUBTYPE) } {
1004 Ok(fcc) => fcc,
1005 Err(why) => {
1006 return Err(NokhwaError::GetPropertyError {
1007 property: "MF_MT_SUBTYPE".to_string(),
1008 error: why.to_string(),
1009 })
1010 }
1011 };
1012
1013 let frame_fmt = match guid_to_frameformat(fourcc) {
1014 Some(fcc) => fcc,
1015 None => continue,
1016 };
1017
1018 if frame_fmt != format.format() {
1019 continue;
1020 }
1021
1022 let (width, height) = match unsafe { media_type.GetUINT64(&MF_MT_FRAME_SIZE) } {
1023 Ok(res_u64) => {
1024 let width = (res_u64 >> 32) as u32;
1025 let height = res_u64 as u32; (width, height)
1027 }
1028 Err(why) => {
1029 return Err(NokhwaError::GetPropertyError {
1030 property: "MF_MT_FRAME_SIZE".to_string(),
1031 error: why.to_string(),
1032 })
1033 }
1034 };
1035
1036 if (Resolution {
1037 width_x: width,
1038 height_y: height,
1039 }) != format.resolution()
1040 {
1041 continue;
1042 }
1043
1044 let framerate_list = {
1046 let mut framerates = vec![0_u32; 3];
1047 if let Ok(fraction_u64) =
1048 unsafe { media_type.GetUINT64(&MF_MT_FRAME_RATE_RANGE_MAX) }
1049 {
1050 let mut numerator = (fraction_u64 >> 32) as u32;
1051 let denominator = fraction_u64 as u32;
1052 if denominator != 1 {
1053 numerator = 0;
1054 }
1055 framerates.push(numerator);
1056 };
1057 if let Ok(fraction_u64) = unsafe { media_type.GetUINT64(&MF_MT_FRAME_RATE) } {
1058 let mut numerator = (fraction_u64 >> 32) as u32;
1059 let denominator = fraction_u64 as u32;
1060 if denominator != 1 {
1061 numerator = 0;
1062 }
1063 framerates.push(numerator);
1064 };
1065 if let Ok(fraction_u64) =
1066 unsafe { media_type.GetUINT64(&MF_MT_FRAME_RATE_RANGE_MIN) }
1067 {
1068 let mut numerator = (fraction_u64 >> 32) as u32;
1069 let denominator = fraction_u64 as u32;
1070 if denominator != 1 {
1071 numerator = 0;
1072 }
1073 framerates.push(numerator);
1074 };
1075 framerates
1076 };
1077
1078 for frame_rate in framerate_list {
1079 if frame_rate == format.frame_rate() {
1080 let result = unsafe {
1081 self.source_reader.SetCurrentMediaType(
1082 MEDIA_FOUNDATION_FIRST_VIDEO_STREAM,
1083 None,
1084 &media_type,
1085 )
1086 };
1087
1088 match result {
1089 Ok(_) => {
1090 self.device_format = format;
1091 self.format_refreshed()?;
1092 return Ok(());
1093 }
1094 Err(why) => {
1095 last_error = Some(NokhwaError::SetPropertyError {
1096 property: "MEDIA_FOUNDATION_FIRST_VIDEO_STREAM".to_string(),
1097 value: format!("{media_type:?}"),
1098 error: why.to_string(),
1099 });
1100 }
1101 }
1102 }
1103 }
1104 }
1105
1106 if let Some(err) = last_error {
1107 return Err(err);
1108 }
1109
1110 Err(NokhwaError::InitializeError {
1111 backend: ApiBackend::MediaFoundation,
1112 error: "Failed to fulfill requested format".to_string(),
1113 })
1114 }
1115
1116 pub fn is_stream_open(&self) -> bool {
1117 self.is_open.get()
1118 }
1119
1120 pub fn start_stream(&mut self) -> Result<(), NokhwaError> {
1121 if let Err(why) = unsafe {
1122 self.source_reader
1123 .SetStreamSelection(MEDIA_FOUNDATION_FIRST_VIDEO_STREAM, true)
1124 } {
1125 return Err(NokhwaError::OpenStreamError(why.to_string()));
1126 }
1127
1128 self.is_open.set(true);
1129 Ok(())
1130 }
1131
1132 pub fn raw_bytes(&mut self) -> Result<Cow<'_, [u8]>, NokhwaError> {
1133 let mut imf_sample: Option<IMFSample> = match unsafe { MFCreateSample() } {
1134 Ok(sample) => Some(sample),
1135 Err(why) => {
1136 return Err(NokhwaError::ReadFrameError(why.to_string()));
1137 }
1138 };
1139 let mut stream_flags = 0;
1140 {
1141 loop {
1142 if let Err(why) = unsafe {
1143 self.source_reader.ReadSample(
1144 MEDIA_FOUNDATION_FIRST_VIDEO_STREAM,
1145 0,
1146 None,
1147 Some(&mut stream_flags),
1148 None,
1149 Some(&mut imf_sample),
1150 )
1151 } {
1152 return Err(NokhwaError::ReadFrameError(why.to_string()));
1153 }
1154
1155 if imf_sample.is_some() {
1156 break;
1157 }
1158 }
1159 }
1160
1161 let imf_sample = match imf_sample {
1162 Some(sample) => sample,
1163 None => {
1164 return Err(NokhwaError::ReadFrameError("No sample".to_string()));
1166 }
1167 };
1168
1169 let buffer = match unsafe { imf_sample.ConvertToContiguousBuffer() } {
1170 Ok(buf) => buf,
1171 Err(why) => return Err(NokhwaError::ReadFrameError(why.to_string())),
1172 };
1173
1174 let mut buffer_valid_length = 0;
1175 let mut buffer_start_ptr = std::ptr::null_mut::<u8>();
1176
1177 if let Err(why) =
1178 unsafe { buffer.Lock(&mut buffer_start_ptr, None, Some(&mut buffer_valid_length)) }
1179 {
1180 return Err(NokhwaError::ReadFrameError(why.to_string()));
1181 }
1182
1183 if buffer_start_ptr.is_null() {
1184 return Err(NokhwaError::ReadFrameError(
1185 "Buffer Pointer Null".to_string(),
1186 ));
1187 }
1188
1189 if buffer_valid_length == 0 {
1190 return Err(NokhwaError::ReadFrameError("Buffer Size is 0".to_string()));
1191 }
1192
1193 let mut data_slice = Vec::with_capacity(buffer_valid_length as usize);
1194
1195 unsafe {
1196 data_slice.extend_from_slice(std::slice::from_raw_parts_mut(
1198 buffer_start_ptr,
1199 buffer_valid_length as usize,
1200 ) as &[u8]);
1201 }
1202
1203 Ok(Cow::from(data_slice))
1204 }
1205
1206 pub fn stop_stream(&mut self) {
1207 self.is_open.set(false);
1208 }
1209 }
1210
1211 impl Drop for MediaFoundationDevice {
1212 fn drop(&mut self) {
1213 unsafe {
1215 if self
1216 .source_reader
1217 .Flush(MEDIA_FOUNDATION_FIRST_VIDEO_STREAM)
1218 .is_ok()
1219 {}
1220
1221 if CAMERA_REFCNT.load(Ordering::SeqCst) > 0 {
1223 CAMERA_REFCNT.store(CAMERA_REFCNT.load(Ordering::SeqCst) - 1, Ordering::SeqCst);
1224 }
1225 if CAMERA_REFCNT.load(Ordering::SeqCst) == 0 {
1226 #[allow(clippy::let_underscore_drop)]
1227 let _ = de_initialize_mf();
1228 }
1229 }
1230 }
1231 }
1232}
1233
1234#[cfg(any(not(windows), feature = "docs-only"))]
1235#[allow(clippy::missing_errors_doc)]
1236#[allow(clippy::unused_self)]
1237#[allow(clippy::needless_pass_by_value)]
1238#[allow(clippy::must_use_candidate)]
1239pub mod wmf {
1240 use nokhwa_core::error::NokhwaError;
1241 use nokhwa_core::types::{
1242 CameraControl, CameraFormat, CameraIndex, CameraInfo, ControlValueSetter,
1243 KnownCameraControl,
1244 };
1245 use std::borrow::Cow;
1246
1247 pub fn initialize_mf() -> Result<(), NokhwaError> {
1248 Err(NokhwaError::NotImplementedError(
1249 "Not on windows".to_string(),
1250 ))
1251 }
1252
1253 pub fn de_initialize_mf() -> Result<(), NokhwaError> {
1254 Err(NokhwaError::NotImplementedError(
1255 "Not on windows".to_string(),
1256 ))
1257 }
1258
1259 pub fn query_msmf() -> Result<Vec<CameraInfo>, NokhwaError> {
1260 Err(NokhwaError::NotImplementedError(
1261 "Not on windows".to_string(),
1262 ))
1263 }
1264
1265 pub struct MediaFoundationDevice {
1266 camera: CameraIndex,
1267 }
1268
1269 impl MediaFoundationDevice {
1270 pub fn new(_index: CameraIndex) -> Result<Self, NokhwaError> {
1271 Ok(MediaFoundationDevice {
1272 camera: CameraIndex::Index(0),
1273 })
1274 }
1275
1276 pub fn index(&self) -> &CameraIndex {
1277 &self.camera
1278 }
1279
1280 pub fn name(&self) -> String {
1281 String::new()
1282 }
1283
1284 pub fn symlink(&self) -> String {
1285 String::new()
1286 }
1287
1288 pub fn compatible_format_list(&mut self) -> Result<Vec<CameraFormat>, NokhwaError> {
1289 Err(NokhwaError::NotImplementedError(
1290 "Only on Windows".to_string(),
1291 ))
1292 }
1293
1294 pub fn control(&self, _control: KnownCameraControl) -> Result<CameraControl, NokhwaError> {
1295 Err(NokhwaError::NotImplementedError(
1296 "Only on Windows".to_string(),
1297 ))
1298 }
1299
1300 pub fn set_control(
1301 &mut self,
1302 _control: KnownCameraControl,
1303 _value: ControlValueSetter,
1304 ) -> Result<(), NokhwaError> {
1305 Err(NokhwaError::NotImplementedError(
1306 "Only on Windows".to_string(),
1307 ))
1308 }
1309
1310 pub fn format_refreshed(&mut self) -> Result<CameraFormat, NokhwaError> {
1311 Err(NokhwaError::NotImplementedError(
1312 "Only on Windows".to_string(),
1313 ))
1314 }
1315
1316 pub fn format(&self) -> CameraFormat {
1317 CameraFormat::default()
1318 }
1319
1320 pub fn set_format(&mut self, _format: CameraFormat) -> Result<(), NokhwaError> {
1321 Err(NokhwaError::NotImplementedError(
1322 "Only on Windows".to_string(),
1323 ))
1324 }
1325
1326 pub fn is_stream_open(&self) -> bool {
1327 false
1328 }
1329
1330 pub fn start_stream(&mut self) -> Result<(), NokhwaError> {
1331 Err(NokhwaError::NotImplementedError(
1332 "Only on Windows".to_string(),
1333 ))
1334 }
1335
1336 pub fn raw_bytes(&mut self) -> Result<Cow<'_, [u8]>, NokhwaError> {
1337 Err(NokhwaError::NotImplementedError(
1338 "Only on Windows".to_string(),
1339 ))
1340 }
1341
1342 pub fn stop_stream(&mut self) {}
1343 }
1344
1345 impl Drop for MediaFoundationDevice {
1346 fn drop(&mut self) {}
1347 }
1348}