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