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