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