Skip to main content

media_device/windows/
media_foundation.rs

1use std::{
2    mem::MaybeUninit,
3    num::NonZeroU32,
4    ptr::null_mut,
5    slice::{from_raw_parts, Iter, IterMut},
6    sync::{
7        atomic::{AtomicBool, Ordering::SeqCst},
8        Arc, Condvar, Mutex, Weak,
9    },
10    time::Duration,
11};
12
13use media_core::{
14    error::Error,
15    failed_error,
16    frame::Frame,
17    none_param_error, not_found_error,
18    time::NSEC_PER_MSEC,
19    unsupported_error,
20    variant::Variant,
21    video::{ColorRange, CompressionFormat, Origin, PixelFormat, VideoFormat, VideoFrameDescriptor},
22    Result,
23};
24use windows::{
25    core::{implement, AsImpl, Interface, GUID, PWSTR},
26    Win32::{
27        Media::MediaFoundation::{
28            IMF2DBuffer, IMF2DBuffer2, IMFActivate, IMFAttributes, IMFMediaBuffer, IMFMediaEvent, IMFMediaSource, IMFMediaType, IMFSample,
29            IMFSourceReader, IMFSourceReaderCallback, IMFSourceReaderCallback_Impl, MF2DBuffer_LockFlags_Read, MFCreateAttributes,
30            MFCreateSourceReaderFromMediaSource, MFEnumDeviceSources, MFGetStrideForBitmapInfoHeader, MFMediaType_Video, MFNominalRange,
31            MFNominalRange_Normal, MFNominalRange_Wide, MFShutdown, MFStartup, MFVideoFormat_ARGB32, MFVideoFormat_I420, MFVideoFormat_MJPG,
32            MFVideoFormat_NV12, MFVideoFormat_RGB24, MFVideoFormat_UYVY, MFVideoFormat_YUY2, MFVideoFormat_YV12, MFSTARTUP_LITE,
33            MF_DEVSOURCE_ATTRIBUTE_FRIENDLY_NAME, MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE, MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID,
34            MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK, MF_MT_DEFAULT_STRIDE, MF_MT_FRAME_RATE, MF_MT_FRAME_SIZE, MF_MT_MAJOR_TYPE,
35            MF_MT_SUBTYPE, MF_MT_VIDEO_NOMINAL_RANGE, MF_READWRITE_DISABLE_CONVERTERS, MF_SOURCE_READER_ASYNC_CALLBACK,
36            MF_SOURCE_READER_FIRST_VIDEO_STREAM, MF_VERSION,
37        },
38        System::Com::{CoInitializeEx, CoTaskMemFree, CoUninitialize, COINIT_APARTMENTTHREADED, COINIT_DISABLE_OLE1DDE},
39    },
40};
41
42use crate::{camera::CameraFormat, Device, DeviceEvent, DeviceEventHandler, DeviceInformation, DeviceManager, OutputDevice, OutputHandler};
43
44pub struct MediaFoundationDeviceManager {
45    devices: Option<Vec<MediaFoundationDevice>>,
46    handler: Option<DeviceEventHandler>,
47}
48
49impl DeviceManager for MediaFoundationDeviceManager {
50    type DeviceType = MediaFoundationDevice;
51    type Iter<'a>
52        = Iter<'a, MediaFoundationDevice>
53    where
54        Self: 'a;
55    type IterMut<'a>
56        = IterMut<'a, MediaFoundationDevice>
57    where
58        Self: 'a;
59
60    fn init() -> Result<Self>
61    where
62        Self: Sized,
63    {
64        unsafe {
65            CoInitializeEx(None, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE)
66                .ok()
67                .map_err(|err| Error::InitializationFailed(err.message().into()))?;
68            MFStartup(MF_VERSION, MFSTARTUP_LITE).map_err(|err| Error::InitializationFailed(err.message().into()))?;
69        }
70        Ok(Self {
71            devices: None,
72            handler: None,
73        })
74    }
75
76    fn deinit(&mut self) {
77        unsafe {
78            MFShutdown().ok();
79            CoUninitialize();
80        }
81    }
82
83    fn index(&self, index: usize) -> Option<&Self::DeviceType> {
84        self.devices.as_ref().and_then(|devices| devices.get(index))
85    }
86
87    fn index_mut(&mut self, index: usize) -> Option<&mut Self::DeviceType> {
88        self.devices.as_mut().and_then(|devices| devices.get_mut(index))
89    }
90
91    fn lookup(&self, id: &str) -> Option<&Self::DeviceType> {
92        self.devices.as_ref().and_then(|devices| devices.iter().find(|device| device.info.id == id))
93    }
94
95    fn lookup_mut(&mut self, id: &str) -> Option<&mut Self::DeviceType> {
96        self.devices.as_mut().and_then(|devices| devices.iter_mut().find(|device| device.info.id == id))
97    }
98
99    fn iter(&self) -> Iter<'_, MediaFoundationDevice> {
100        self.devices.as_deref().unwrap_or(&[]).iter()
101    }
102
103    fn iter_mut(&mut self) -> IterMut<'_, MediaFoundationDevice> {
104        self.devices.as_deref_mut().unwrap_or(&mut []).iter_mut()
105    }
106
107    fn refresh(&mut self) -> Result<()> {
108        let device_sources = Self::get_device_sources()?;
109        let mut devices = Vec::with_capacity(device_sources.len());
110
111        for (index, activate) in device_sources.iter().enumerate() {
112            let dev_info = DeviceInformation::from_source_activate(activate)?;
113            devices.push(MediaFoundationDevice::new(dev_info, index)?);
114        }
115
116        let count = devices.len();
117        self.devices = Some(devices);
118        if let Some(handler) = &self.handler {
119            handler(&DeviceEvent::Refreshed(count));
120        }
121        Ok(())
122    }
123
124    fn set_change_handler<F>(&mut self, handler: F) -> Result<()>
125    where
126        F: Fn(&DeviceEvent) + Send + Sync + 'static,
127    {
128        self.handler = Some(Box::new(handler));
129        Ok(())
130    }
131}
132
133impl Default for MediaFoundationDeviceManager {
134    fn default() -> Self {
135        Self::new()
136    }
137}
138
139impl MediaFoundationDeviceManager {
140    pub fn new() -> Self {
141        Self {
142            devices: None,
143            handler: None,
144        }
145    }
146
147    fn get_device_sources() -> Result<Vec<IMFActivate>> {
148        let attributes: IMFAttributes = unsafe {
149            let mut attributes: Option<IMFAttributes> = None;
150            MFCreateAttributes(&mut attributes, 1).map_err(|err| Error::CreationFailed(err.message().into()))?;
151            attributes.ok_or_else(|| none_param_error!(attributes))?
152        };
153
154        unsafe {
155            attributes
156                .SetGUID(&MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE, &MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID)
157                .map_err(|err| Error::SetFailed(err.message().into()))?;
158        }
159
160        let mut source_activate_ptr: MaybeUninit<*mut Option<IMFActivate>> = MaybeUninit::uninit();
161        let mut source_activate_count: u32 = 0;
162        unsafe {
163            MFEnumDeviceSources(&attributes, source_activate_ptr.as_mut_ptr(), &mut source_activate_count)
164                .map_err(|err| failed_error!(err.message()))?;
165        }
166
167        let mut device_sources = vec![];
168
169        if source_activate_count > 0 {
170            unsafe { from_raw_parts(source_activate_ptr.assume_init(), source_activate_count as usize) }.iter().for_each(|ptr| {
171                if let Some(activate) = ptr {
172                    device_sources.push(activate.clone());
173                }
174            });
175        };
176
177        Ok(device_sources)
178    }
179}
180
181impl DeviceInformation {
182    fn from_source_activate(activate: &IMFActivate) -> Result<Self> {
183        let mut symbolic_link_ptr = PWSTR(null_mut());
184        let mut symbolic_link_len = 0;
185        unsafe {
186            activate
187                .GetAllocatedString(&MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK, &mut symbolic_link_ptr, &mut symbolic_link_len)
188                .map_err(|err| failed_error!(err.message()))?;
189        }
190        if symbolic_link_ptr.is_null() {
191            return Err(none_param_error!(symbolic_link_ptr));
192        }
193        let id = unsafe {
194            let symbolic_link = symbolic_link_ptr.to_string().map_err(|err| failed_error!(err.to_string()));
195            CoTaskMemFree(Some(symbolic_link_ptr.as_ptr() as _));
196            symbolic_link?
197        };
198
199        let mut friendly_name_ptr = PWSTR(null_mut());
200        let mut friendly_name_len = 0;
201        unsafe {
202            activate
203                .GetAllocatedString(&MF_DEVSOURCE_ATTRIBUTE_FRIENDLY_NAME, &mut friendly_name_ptr, &mut friendly_name_len)
204                .map_err(|err| failed_error!(err.message()))?;
205        }
206        if friendly_name_ptr.is_null() {
207            return Err(none_param_error!(friendly_name_ptr));
208        }
209        let name = unsafe {
210            let name = friendly_name_ptr.to_string().map_err(|err| failed_error!(err.to_string()));
211            CoTaskMemFree(Some(friendly_name_ptr.as_ptr() as _));
212            name?
213        };
214
215        Ok(Self {
216            id,
217            name,
218        })
219    }
220}
221
222struct BufferLockGuard<'a> {
223    buffer: &'a IMFMediaBuffer,
224    buffer_2d: Option<IMF2DBuffer>,
225    data: &'a [u8],
226    stride: i32,
227}
228
229impl<'a> BufferLockGuard<'a> {
230    fn new(buffer: &'a IMFMediaBuffer, height: NonZeroU32) -> windows_core::Result<Self> {
231        let buffer_2d = buffer.cast::<IMF2DBuffer>();
232        let mut buffer_ptr = null_mut::<u8>();
233        let mut buffer_stride = 0;
234        let mut buffer_length = 0;
235
236        let mut result = match &buffer_2d {
237            Ok(buffer_2d_ref) => {
238                let mut scanline_ptr = null_mut::<u8>();
239                let mut result = match buffer.cast::<IMF2DBuffer2>() {
240                    Ok(buffer_2d_2) => unsafe {
241                        buffer_2d_2.Lock2DSize(MF2DBuffer_LockFlags_Read, &mut scanline_ptr, &mut buffer_stride, &mut buffer_ptr, &mut buffer_length)
242                    },
243                    Err(err) => Err(err),
244                };
245
246                if result.is_err() {
247                    unsafe {
248                        result = buffer_2d_ref.Lock2D(&mut scanline_ptr, &mut buffer_stride);
249                        if result.is_ok() {
250                            if buffer_stride < 0 {
251                                buffer_ptr = scanline_ptr.offset((height.get() - 1) as isize * buffer_stride as isize);
252                            } else {
253                                buffer_ptr = scanline_ptr;
254                            }
255
256                            match buffer.GetCurrentLength() {
257                                Ok(length) => buffer_length = length,
258                                Err(err) => {
259                                    result = Err(err);
260                                    buffer_2d_ref.Unlock2D().ok();
261                                }
262                            }
263                        }
264                    };
265                }
266
267                result
268            }
269            Err(err) => Err(err.clone()),
270        };
271
272        let buffer_2d = if result.is_err() {
273            result = unsafe { buffer.Lock(&mut buffer_ptr, None, Some(&mut buffer_length)) };
274            None
275        } else {
276            buffer_2d.ok()
277        };
278
279        result.map(|_| {
280            let data = unsafe { from_raw_parts(buffer_ptr, buffer_length as usize) };
281            Self {
282                buffer,
283                buffer_2d,
284                data,
285                stride: buffer_stride,
286            }
287        })
288    }
289}
290
291impl Drop for BufferLockGuard<'_> {
292    fn drop(&mut self) {
293        if let Some(buffer_2d) = &self.buffer_2d {
294            unsafe {
295                buffer_2d.Unlock2D().ok();
296            }
297        } else {
298            unsafe {
299                self.buffer.Unlock().ok();
300            }
301        }
302    }
303}
304
305#[implement(IMFSourceReaderCallback)]
306struct SourceReaderCallback {
307    info: DeviceInformation,
308    current_format: Option<CameraFormat>,
309    stride_cache: Option<i32>,
310    handler: OutputHandler,
311    source_reader: Weak<Mutex<IMFSourceReader>>,
312    running: AtomicBool,
313    signal: Option<Arc<(Mutex<bool>, Condvar)>>,
314}
315
316impl SourceReaderCallback {
317    pub fn new(info: DeviceInformation, handler: OutputHandler) -> Self {
318        Self {
319            info,
320            current_format: None,
321            stride_cache: None,
322            handler,
323            source_reader: Weak::new(),
324            running: AtomicBool::new(false),
325            signal: None,
326        }
327    }
328
329    pub fn get_default_stride(&self) -> i32 {
330        self.stride_cache.unwrap_or(0)
331    }
332
333    pub fn set_default_stride(&mut self, stride: i32) {
334        self.stride_cache = Some(stride);
335    }
336
337    pub fn set_source_reader(&mut self, source_reader: &Arc<Mutex<IMFSourceReader>>) {
338        self.source_reader = Arc::downgrade(source_reader);
339    }
340
341    pub fn set_current_format(&mut self, current_format: CameraFormat) {
342        self.current_format = Some(current_format);
343    }
344
345    pub fn set_running(&self, running: bool) {
346        self.running.store(running, SeqCst);
347    }
348
349    pub fn set_signal(&mut self, signal: Option<Arc<(Mutex<bool>, Condvar)>>) {
350        self.signal = signal;
351    }
352}
353
354impl IMFSourceReaderCallback_Impl for SourceReaderCallback_Impl {
355    fn OnReadSample(
356        &self,
357        hrstatus: windows_core::HRESULT,
358        _dwstreamindex: u32,
359        _dwstreamflags: u32,
360        lltimestamp: i64,
361        psample: windows_core::Ref<'_, IMFSample>,
362    ) -> windows_core::Result<()> {
363        if hrstatus.is_err() || !self.running.load(SeqCst) {
364            return Ok(());
365        }
366
367        let current_format = match &self.current_format {
368            Some(format) => format,
369            None => return Ok(()),
370        };
371
372        let width = match NonZeroU32::new(current_format.width) {
373            Some(width) => width,
374            None => return Ok(()),
375        };
376        let height = match NonZeroU32::new(current_format.height) {
377            Some(height) => height,
378            None => return Ok(()),
379        };
380        let pixel_format = match current_format.format {
381            VideoFormat::Pixel(format) => format,
382            _ => return Ok(()),
383        };
384
385        let buffer = psample.as_ref().and_then(|sample| unsafe { sample.ConvertToContiguousBuffer().ok() });
386
387        if let Some(buffer) = buffer {
388            if let Ok(locked_buffer) = BufferLockGuard::new(&buffer, height) {
389                let stride = if locked_buffer.stride == 0 {
390                    self.get_default_stride()
391                } else {
392                    locked_buffer.stride
393                };
394
395                let (stride, origin) = if stride >= 0 {
396                    (stride as u32, Origin::TopDown)
397                } else {
398                    (-stride as u32, Origin::BottomUp)
399                };
400
401                let mut desc = VideoFrameDescriptor::new(pixel_format, width, height);
402                desc.color_range = current_format.color_range;
403                desc.origin = origin;
404
405                let video_frame = if stride != 0 {
406                    Frame::video_creator().create_from_aligned_buffer_with_descriptor(desc, NonZeroU32::new(stride).unwrap(), locked_buffer.data)
407                } else {
408                    Frame::video_creator().create_from_buffer_with_descriptor(desc, locked_buffer.data)
409                };
410
411                if let Ok(mut video_frame) = video_frame {
412                    video_frame.source = Some(self.info.id.clone());
413                    video_frame.pts = Some(lltimestamp * 100 / NSEC_PER_MSEC as i64); // lltimestamp is in 100ns units
414                    let handler = self.handler.as_ref();
415                    handler(video_frame).ok();
416                }
417            }
418        };
419
420        let source_reader = self.source_reader.upgrade();
421        if let Some(source_reader) = source_reader {
422            unsafe {
423                source_reader.lock().unwrap().ReadSample(MF_SOURCE_READER_FIRST_VIDEO_STREAM.0 as u32, 0, None, None, None, None).ok();
424            }
425        };
426
427        Ok(())
428    }
429
430    fn OnFlush(&self, _dwstreamindex: u32) -> windows_core::Result<()> {
431        if let Some(signal) = &self.signal {
432            let (lock, condvar) = &**signal;
433            let mut flushed = lock.lock().unwrap();
434            *flushed = true;
435            condvar.notify_one();
436        }
437        Ok(())
438    }
439
440    fn OnEvent(&self, _dwstreamindex: u32, _pevent: windows_core::Ref<'_, IMFMediaEvent>) -> windows_core::Result<()> {
441        Ok(())
442    }
443}
444
445fn from_mf_video_format(subtype: GUID) -> Option<VideoFormat> {
446    #[allow(non_snake_case, non_upper_case_globals)]
447    match subtype {
448        MFVideoFormat_I420 => Some(VideoFormat::Pixel(PixelFormat::I420)),
449        MFVideoFormat_YUY2 => Some(VideoFormat::Pixel(PixelFormat::YUYV)),
450        MFVideoFormat_UYVY => Some(VideoFormat::Pixel(PixelFormat::UYVY)),
451        MFVideoFormat_ARGB32 => Some(VideoFormat::Pixel(PixelFormat::ARGB32)),
452        MFVideoFormat_RGB24 => Some(VideoFormat::Pixel(PixelFormat::RGB24)),
453        MFVideoFormat_MJPG => Some(VideoFormat::Compression(CompressionFormat::MJPEG)),
454        MFVideoFormat_NV12 => Some(VideoFormat::Pixel(PixelFormat::NV12)),
455        MFVideoFormat_YV12 => Some(VideoFormat::Pixel(PixelFormat::YV12)),
456        _ => None,
457    }
458}
459
460fn get_radio(media_type: &IMFMediaType, key: &GUID) -> Result<f32> {
461    let (numerator, denominator) = match unsafe { media_type.GetUINT64(key) } {
462        Ok(value) => {
463            let numerator = (value >> 32) as u32;
464            let denominator = value as u32;
465            (numerator, denominator)
466        }
467        Err(err) => return Err(Error::GetFailed(err.message().into())),
468    };
469
470    Ok(numerator as f32 / denominator as f32)
471}
472
473fn from_mf_media_type(media_type: &IMFMediaType) -> Option<CameraFormat> {
474    if let Ok(major_type) = unsafe { media_type.GetGUID(&MF_MT_MAJOR_TYPE) } {
475        if major_type != MFMediaType_Video {
476            return None;
477        }
478    } else {
479        return None;
480    }
481
482    let subtype = match unsafe { media_type.GetGUID(&MF_MT_SUBTYPE) } {
483        Ok(subtype) => subtype,
484        Err(_) => return None,
485    };
486
487    let format = from_mf_video_format(subtype)?;
488
489    let color_range = match unsafe { media_type.GetUINT32(&MF_MT_VIDEO_NOMINAL_RANGE) } {
490        #[allow(non_upper_case_globals)]
491        Ok(range) => match MFNominalRange(range as i32) {
492            MFNominalRange_Normal => ColorRange::Full,
493            MFNominalRange_Wide => ColorRange::Video,
494            _ => ColorRange::Unspecified,
495        },
496        Err(_) => ColorRange::Unspecified,
497    };
498
499    let (width, height) = match unsafe { media_type.GetUINT64(&MF_MT_FRAME_SIZE) } {
500        Ok(value) => {
501            let width = (value >> 32) as u32;
502            let height = value as u32;
503            (width, height)
504        }
505        Err(_) => return None,
506    };
507
508    let frame_rate = match get_radio(media_type, &MF_MT_FRAME_RATE) {
509        Ok(frame_rate) => frame_rate,
510        Err(_) => return None,
511    };
512
513    Some(CameraFormat {
514        format,
515        color_range,
516        width,
517        height,
518        frame_rates: vec![frame_rate],
519    })
520}
521
522fn get_formats(source_reader: &IMFSourceReader) -> Result<Vec<CameraFormat>> {
523    let mut video_formats = vec![];
524    let mut index = 0;
525
526    while let Ok(media_type) = unsafe { source_reader.GetNativeMediaType(MF_SOURCE_READER_FIRST_VIDEO_STREAM.0 as u32, index) } {
527        if let Some(camera_format) = from_mf_media_type(&media_type) {
528            video_formats.push(camera_format);
529        }
530        index += 1;
531    }
532
533    Ok(video_formats)
534}
535
536fn get_current_format(source_reader: &IMFSourceReader) -> Result<CameraFormat> {
537    let media_type = unsafe { source_reader.GetCurrentMediaType(MF_SOURCE_READER_FIRST_VIDEO_STREAM.0 as u32) };
538    let media_type = media_type.map_err(|err| Error::GetFailed(err.message().into()))?;
539    let camera_format = from_mf_media_type(&media_type).ok_or_else(|| unsupported_error!(media_type))?;
540    Ok(camera_format)
541}
542
543fn get_default_stride_with_width(source_reader: &IMFSourceReader, width: u32) -> Option<i32> {
544    let media_type = unsafe { source_reader.GetCurrentMediaType(MF_SOURCE_READER_FIRST_VIDEO_STREAM.0 as u32) }.ok()?;
545    let stride = unsafe { media_type.GetUINT32(&MF_MT_DEFAULT_STRIDE) }.ok().map(|s| s as i32);
546
547    if stride.is_some() {
548        return stride;
549    }
550
551    let sub_type = unsafe { media_type.GetGUID(&MF_MT_SUBTYPE) }.ok()?;
552    let stride = unsafe { MFGetStrideForBitmapInfoHeader(sub_type.data1, width).ok() };
553
554    if let Some(stride) = stride {
555        unsafe { media_type.SetUINT32(&MF_MT_DEFAULT_STRIDE, stride as u32) }.ok();
556    }
557
558    stride
559}
560
561fn start_internal(source_reader: &IMFSourceReader, callback: &mut SourceReaderCallback, camera_format: &CameraFormat) {
562    callback.set_current_format(camera_format.clone());
563    if let Some(stride) = get_default_stride_with_width(source_reader, camera_format.width) {
564        callback.set_default_stride(stride);
565    }
566
567    unsafe {
568        source_reader.SetStreamSelection(MF_SOURCE_READER_FIRST_VIDEO_STREAM.0 as u32, true).ok();
569        source_reader.ReadSample(MF_SOURCE_READER_FIRST_VIDEO_STREAM.0 as u32, 0, None, None, None, None).ok();
570    }
571
572    callback.set_running(true);
573}
574
575const TIMEOUT_SECONDS: u64 = 1;
576
577fn stop_internal(source_reader: &IMFSourceReader, callback: &mut SourceReaderCallback) -> Result<()> {
578    callback.set_running(false);
579    let signal = Arc::new((Mutex::new(false), Condvar::new()));
580    callback.set_signal(Some(signal.clone()));
581
582    let wait = unsafe {
583        match source_reader.Flush(MF_SOURCE_READER_FIRST_VIDEO_STREAM.0 as u32) {
584            Ok(()) => true,
585            Err(err) => return Err(Error::StopFailed(err.message().into())),
586        }
587    };
588
589    if wait {
590        let (lock, condvar) = &*signal;
591        let flushed = lock.lock().map_err(|err| Error::StopFailed(err.to_string().into()))?;
592        if !*flushed {
593            condvar.wait_timeout(flushed, Duration::from_secs(TIMEOUT_SECONDS)).ok();
594        }
595    }
596
597    Ok(())
598}
599
600const SIMILAR_FORMAT_DIFF: f32 = 1.0;
601const DIFFERENT_FORMAT_DIFF: f32 = 2.0;
602
603fn match_supported_format(
604    source_reader: &IMFSourceReader,
605    width: Option<u32>,
606    height: Option<u32>,
607    video_format: Option<VideoFormat>,
608    frame_rate: Option<f32>,
609) -> Option<IMFMediaType> {
610    let mut matched_media_type: Option<IMFMediaType> = None;
611    let mut min_diff = f32::MAX;
612    let mut index = 0;
613
614    while let Ok(media_type) = unsafe { source_reader.GetNativeMediaType(MF_SOURCE_READER_FIRST_VIDEO_STREAM.0 as u32, index) } {
615        if let Some(camera_format) = from_mf_media_type(&media_type) {
616            let resolution_diff = match (width, height) {
617                (Some(width), Some(height)) => {
618                    (camera_format.width as f32 - width as f32).abs() + (camera_format.height as f32 - height as f32).abs()
619                }
620                _ => 0.0,
621            };
622
623            let frame_rate_diff = match frame_rate {
624                Some(frame_rate) => (camera_format.frame_rates[0] - frame_rate).abs(),
625                None => 0.0,
626            };
627
628            let format_diff = match video_format {
629                Some(video_format) => {
630                    if camera_format.format == video_format {
631                        0.0
632                    } else if camera_format.format.is_yuv() && video_format.is_yuv() {
633                        SIMILAR_FORMAT_DIFF
634                    } else {
635                        DIFFERENT_FORMAT_DIFF
636                    }
637                }
638                None => 0.0,
639            };
640
641            let diff = resolution_diff + frame_rate_diff + format_diff;
642
643            if diff < min_diff {
644                min_diff = diff;
645                matched_media_type = Some(media_type.clone());
646            }
647        }
648        index += 1;
649    }
650
651    matched_media_type
652}
653
654pub struct MediaFoundationDevice {
655    info: DeviceInformation,
656    index: usize,
657    running: bool,
658    formats: Option<Vec<CameraFormat>>,
659    current_format: Option<CameraFormat>,
660    handler: Option<OutputHandler>,
661    source_reader: Option<(Arc<Mutex<IMFSourceReader>>, IMFSourceReaderCallback)>,
662}
663
664impl Device for MediaFoundationDevice {
665    fn name(&self) -> &str {
666        &self.info.name
667    }
668
669    fn id(&self) -> &str {
670        &self.info.id
671    }
672
673    fn start(&mut self) -> Result<()> {
674        let (running, current_format, formats) = {
675            let camera_format = self.current_format.clone();
676            let (source_reader, callback) = self.get_source_reader()?;
677            let source_reader = source_reader.lock().map_err(|err| Error::StartFailed(err.to_string().into()))?;
678
679            if let Some(camera_format) = camera_format {
680                let media_type = match_supported_format(
681                    &source_reader,
682                    Some(camera_format.width),
683                    Some(camera_format.height),
684                    Some(camera_format.format),
685                    Some(camera_format.frame_rates[0]),
686                );
687                if let Some(media_type) = media_type {
688                    unsafe { source_reader.SetCurrentMediaType(MF_SOURCE_READER_FIRST_VIDEO_STREAM.0 as u32, None, &media_type).ok() };
689                }
690            }
691
692            let current_format = get_current_format(&source_reader)?;
693            let formats = get_formats(&source_reader)?;
694
695            start_internal(&source_reader, callback, &current_format);
696
697            (true, current_format, formats)
698        };
699
700        self.running = running;
701        self.current_format = Some(current_format);
702        self.formats = Some(formats);
703
704        Ok(())
705    }
706
707    fn stop(&mut self) -> Result<()> {
708        self.running = false;
709
710        {
711            let (source_reader, callback) = self.get_source_reader()?;
712            let source_reader = source_reader.lock().map_err(|err| Error::StopFailed(err.to_string().into()))?;
713
714            stop_internal(&source_reader, callback)?;
715        }
716
717        self.source_reader = None;
718
719        Ok(())
720    }
721
722    fn configure(&mut self, options: &Variant) -> Result<()> {
723        let width = options["width"].get_uint32();
724        let height = options["height"].get_uint32();
725        let video_format = options["format"].get_uint32();
726        let frame_rate = options["frame-rate"].get_float();
727
728        let video_format = match video_format {
729            Some(video_format) => VideoFormat::try_from(video_format).ok(),
730            None => None,
731        };
732
733        let camera_format = if self.running {
734            let (source_reader, callback) = self.get_source_reader()?;
735            let source_reader = source_reader.lock().map_err(|err| Error::SetFailed(err.to_string().into()))?;
736            let media_type = match_supported_format(&source_reader, width, height, video_format, frame_rate);
737
738            if let Some(media_type) = media_type {
739                match from_mf_media_type(&media_type) {
740                    Some(camera_format) => {
741                        stop_internal(&source_reader, callback).ok();
742                        unsafe { source_reader.SetCurrentMediaType(MF_SOURCE_READER_FIRST_VIDEO_STREAM.0 as u32, None, &media_type).ok() };
743                        callback.set_current_format(camera_format.clone());
744                        start_internal(&source_reader, callback, &camera_format);
745                        Some(camera_format)
746                    }
747                    None => return Err(unsupported_error!("format")),
748                }
749            } else {
750                return Err(unsupported_error!("format"));
751            }
752        } else {
753            Some(CameraFormat {
754                format: video_format.unwrap_or(VideoFormat::Pixel(PixelFormat::NV12)),
755                color_range: ColorRange::default(),
756                width: width.unwrap_or_default(),
757                height: height.unwrap_or_default(),
758                frame_rates: vec![frame_rate.unwrap_or_default()],
759            })
760        };
761
762        self.current_format = camera_format;
763
764        Ok(())
765    }
766
767    fn control(&mut self, _action: &Variant) -> Result<()> {
768        Err(Error::NotImplemented)
769    }
770
771    fn running(&self) -> bool {
772        self.running
773    }
774
775    fn formats(&self) -> Result<Variant> {
776        if !self.running {
777            return Err(Error::NotRunning(self.info.name.clone().into()));
778        }
779
780        let video_formats = self.formats.as_ref().ok_or_else(|| not_found_error!("video formats"))?;
781        let mut formats = Variant::new_array();
782        for video_format in video_formats {
783            let mut format = Variant::new_dict();
784            format["format"] = (Into::<u32>::into(video_format.format)).into();
785            format["width"] = video_format.width.into();
786            format["height"] = video_format.height.into();
787            format["frame-rates"] = video_format.frame_rates.iter().map(|frame_rate| Variant::from(*frame_rate)).collect();
788            formats.array_add(format);
789        }
790
791        Ok(formats)
792    }
793}
794
795impl OutputDevice for MediaFoundationDevice {
796    fn set_output_handler<F>(&mut self, handler: F) -> Result<()>
797    where
798        F: Fn(Frame) -> Result<()> + Send + Sync + 'static,
799    {
800        self.handler = Some(Arc::new(handler));
801        Ok(())
802    }
803}
804
805impl MediaFoundationDevice {
806    fn new(dev_info: DeviceInformation, index: usize) -> Result<Self> {
807        Ok(Self {
808            info: dev_info,
809            index,
810            running: false,
811            current_format: None,
812            formats: None,
813            handler: None,
814            source_reader: None,
815        })
816    }
817
818    fn get_source_reader(&mut self) -> Result<(&Mutex<IMFSourceReader>, &mut SourceReaderCallback)> {
819        if self.source_reader.is_none() {
820            let device_sources = MediaFoundationDeviceManager::get_device_sources()?;
821            let activate = device_sources.get(self.index).ok_or_else(|| not_found_error!(self.index))?;
822            let media_source = unsafe { activate.ActivateObject::<IMFMediaSource>().map_err(|err| Error::OpenFailed(err.message().into()))? };
823
824            let attributes: IMFAttributes = unsafe {
825                let mut attributes: Option<IMFAttributes> = None;
826                MFCreateAttributes(&mut attributes, 1).map_err(|err| Error::CreationFailed(err.message().into()))?;
827                attributes.ok_or_else(|| none_param_error!(attributes))?
828            };
829
830            unsafe {
831                attributes.SetUINT32(&MF_READWRITE_DISABLE_CONVERTERS, true as u32).map_err(|err| Error::SetFailed(err.message().into()))?;
832            }
833
834            let handler = self.handler.as_ref().ok_or_else(|| none_param_error!(output_handler))?;
835            let callback: IMFSourceReaderCallback = SourceReaderCallback::new(self.info.clone(), handler.clone()).into();
836
837            unsafe {
838                attributes.SetUnknown(&MF_SOURCE_READER_ASYNC_CALLBACK, &callback).map_err(|err| Error::SetFailed(err.message().into()))?;
839            }
840
841            let source_reader = unsafe {
842                MFCreateSourceReaderFromMediaSource(&media_source, &attributes).map_err(|err| Error::CreationFailed(err.message().into()))?
843            };
844
845            #[allow(clippy::arc_with_non_send_sync)]
846            let source_reader = Arc::new(Mutex::new(source_reader));
847
848            unsafe {
849                callback.as_impl_ptr().as_mut().set_source_reader(&source_reader);
850            }
851
852            self.source_reader = Some((source_reader.clone(), callback));
853        }
854        let (source_reader, reader_callback) = self.source_reader.as_ref().unwrap();
855        let reader_callback = unsafe { reader_callback.as_impl_ptr().as_mut() };
856        Ok((source_reader.as_ref(), reader_callback))
857    }
858}