media_device/windows/
media_foundation.rs

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