Skip to main content

nokhwa_bindings_windows/
lib.rs

1/*
2 * Copyright 2022 l1npengtul <l1npengtul@protonmail.com> / The Nokhwa Contributors
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *     http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#![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//! # nokhwa-bindings-windows
24//! This crate is the `MediaFoundation` bindings for the `nokhwa` crate.
25//!
26//! It is not meant for general consumption. If you are looking for a Windows camera capture crate, consider using `nokhwa` with feature `input-msmf`.
27//!
28//! No support or API stability will be given. Subject to change at any time.
29
30#[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    // See: https://stackoverflow.com/questions/80160/what-does-coinit-speed-over-memory-do
86    const CO_INIT_APARTMENT_THREADED: COINIT = COINIT(0x2);
87    const CO_INIT_DISABLE_OLE1DDE: COINIT = COINIT(0x4);
88
89    // See: https://gix.github.io/media-types/#major-types
90    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    // const CAM_CTRL_AUTO: i32 = 0x0001;
125    // const CAM_CTRL_MANUAL: i32 = 0x0002;
126
127    // macro_rules! define_controls {
128    //     ( $( ($key:expr => ($property:ident, $min:ident, $max:ident, $step:ident, $default:ident, $flag:ident)) )* ) => {
129    //         $(
130    //         $key => {
131    //             if let Err(why) = unsafe {
132    //                     video_proc_amp.GetRange(
133    //                         $property.0,
134    //                         &mut $min,
135    //                         &mut $max,
136    //                         &mut $step,
137    //                         &mut $default,
138    //                         &mut $flag,
139    //                     )
140    //                 } {
141    //                     return Err(NokhwaError::GetPropertyError {
142    //                         property: stringify!($key).to_string(),
143    //                         error: why.to_string()
144    //                     });
145    //                 }
146    //         }
147    //         )*
148    //     };
149    //     ( $( ($key:expr : ($property:ident, $value:ident, $flag:ident)) )* ) => {
150    //         $(
151    //         $key => {
152    //             if let Err(why) = unsafe {
153    //                 video_proc_amp.Get($property.0, &mut $value, &mut $flag)
154    //                 } {
155    //                     return Err(NokhwaError::GetPropertyError {
156    //                         property: stringify!($key).to_string(),
157    //                         error: why.to_string()
158    //                     });
159    //                 }
160    //         }
161    //         )*
162    //     };
163    // }
164
165    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        /// Wallclock instant captured when the stream was started.
415        /// MF sample timestamps are relative to stream start, so
416        /// `stream_epoch + sample_time` gives us an absolute wallclock.
417        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                    // increment refcnt
495                    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        //
524        // pub fn with_string(unique_id: &[u16]) -> Result<Self, NokhwaError> {
525        //     let devicelist = query_media_foundation_descriptors()?;
526        //     let mut id_eq = None;
527        //
528        //     for mfdev in devicelist {
529        //         if (mfdev.symlink() as &[u16]) == unique_id {
530        //             id_eq = Some(mfdev.index().as_index()?);
531        //             break;
532        //         }
533        //     }
534        //
535        //     match id_eq {
536        //         Some(index) => Self::new(index),
537        //         None => {
538        //             return Err(BindingError::DeviceOpenFailError(
539        //                 std::str::from_utf8(
540        //                     &unique_id.iter().map(|x| *x as u8).collect::<Vec<u8>>(),
541        //                 )
542        //                 .unwrap_or("")
543        //                 .to_string(),
544        //                 "Not Found".to_string(),
545        //             ))
546        //         }
547        //     }
548        // }
549
550        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; // the cast will truncate the upper bits
585                        (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                // MFRatio is represented as 2 u32s in memory. This means we can convert it to 2
596                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            // We need to make sure to use all the original attributes of the IMFMediaType to avoid problems.
998            // Otherwise, constructing IMFMediaType from scratch can sometimes fail due to not exactly matching.
999            // Therefore, we search for the first media_type that matches and also works correctly.
1000
1001            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; // the cast will truncate the upper bits
1032                        (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                // MFRatio is represented as 2 u32s in memory. This means we can convert it to 2
1051                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                    // shouldn't happen
1175                    return Err(NokhwaError::ReadFrameError("No sample".to_string()));
1176                }
1177            };
1178
1179            // Calculate absolute capture timestamp.
1180            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                // Copy pointer because we're bout to drop IMFSample
1216                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            // swallow errors
1234            unsafe {
1235                if self
1236                    .source_reader
1237                    .Flush(MEDIA_FOUNDATION_FIRST_VIDEO_STREAM)
1238                    .is_ok()
1239                {}
1240
1241                // decrement refcnt
1242                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}