Skip to content

Commit 5b86e1d

Browse files
authored
Cherry pick 0.10 fixes to MediaFoundation backend (#200)
* Fix infinite loop when guid_to_frameformat fails (#188) * Fix bug in MSFoundation backend where media formats are created not exactly as expected by the device and fail to open (#197) * Fix infinite loop when guid_to_frameformat fails * Fix bug in MSFoundation backend where media formats are created not exactly as expected by the device and fail to open. Also fix conversion of MSFoundation RGB24, it's RAWBGR instead of RAWRGB * Fix compilation issue
1 parent 33f0787 commit 5b86e1d

File tree

1 file changed

+121
-105
lines changed
  • nokhwa-bindings-windows/src

1 file changed

+121
-105
lines changed

nokhwa-bindings-windows/src/lib.rs

Lines changed: 121 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ pub mod wmf {
4949
use nokhwa_core::control::{CameraControl, ControlValueDescription, ControlValue, KnownCameraControl};
5050
use windows::Win32::Media::DirectShow::{CameraControl_Flags_Auto, CameraControl_Flags_Manual};
5151
use windows::Win32::Media::MediaFoundation::{
52-
IMFMediaType, MFCreateSample, MF_SOURCE_READER_FIRST_VIDEO_STREAM,
52+
MFCreateSample, MF_SOURCE_READER_FIRST_VIDEO_STREAM,
5353
};
5454
use windows::{
5555
core::{Interface, GUID, PWSTR},
@@ -66,14 +66,14 @@ pub mod wmf {
6666
KernelStreaming::GUID_NULL,
6767
MediaFoundation::{
6868
IMFActivate, IMFAttributes, IMFMediaSource, IMFSample, IMFSourceReader,
69-
MFCreateAttributes, MFCreateMediaType, MFCreateSourceReaderFromMediaSource,
70-
MFEnumDeviceSources, MFMediaType_Video, MFShutdown, MFStartup,
69+
MFCreateAttributes, MFCreateSourceReaderFromMediaSource,
70+
MFEnumDeviceSources, MFShutdown, MFStartup,
7171
MFSTARTUP_NOSOCKET, MF_API_VERSION, MF_DEVSOURCE_ATTRIBUTE_FRIENDLY_NAME,
7272
MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE,
7373
MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID,
7474
MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK, MF_MT_FRAME_RATE,
7575
MF_MT_FRAME_RATE_RANGE_MAX, MF_MT_FRAME_RATE_RANGE_MIN, MF_MT_FRAME_SIZE,
76-
MF_MT_MAJOR_TYPE, MF_MT_SUBTYPE, MF_READWRITE_DISABLE_CONVERTERS,
76+
MF_MT_SUBTYPE, MF_READWRITE_DISABLE_CONVERTERS,
7777
},
7878
},
7979
System::Com::{CoInitializeEx, CoUninitialize, COINIT},
@@ -166,24 +166,14 @@ pub mod wmf {
166166
fn guid_to_frameformat(guid: GUID) -> Option<FrameFormat> {
167167
match guid {
168168
MF_VIDEO_FORMAT_NV12 => Some(FrameFormat::NV12),
169-
MF_VIDEO_FORMAT_RGB24 => Some(FrameFormat::RAWRGB),
169+
MF_VIDEO_FORMAT_RGB24 => Some(FrameFormat::RAWBGR),
170170
MF_VIDEO_FORMAT_GRAY => Some(FrameFormat::GRAY),
171171
MF_VIDEO_FORMAT_YUY2 => Some(FrameFormat::YUYV),
172172
MF_VIDEO_FORMAT_MJPEG => Some(FrameFormat::MJPEG),
173173
_ => None,
174174
}
175175
}
176176

177-
fn frameformat_to_guid(frameformat: FrameFormat) -> GUID {
178-
match frameformat {
179-
FrameFormat::MJPEG => MF_VIDEO_FORMAT_MJPEG,
180-
FrameFormat::YUYV => MF_VIDEO_FORMAT_YUY2,
181-
FrameFormat::NV12 => MF_VIDEO_FORMAT_NV12,
182-
FrameFormat::GRAY => MF_VIDEO_FORMAT_GRAY,
183-
FrameFormat::RAWRGB => MF_VIDEO_FORMAT_RGB24,
184-
}
185-
}
186-
187177
pub fn initialize_mf() -> Result<(), NokhwaError> {
188178
if !(INITIALIZED.load(Ordering::SeqCst)) {
189179
if let Err(why) = unsafe {
@@ -575,6 +565,7 @@ pub mod wmf {
575565
self.source_reader
576566
.GetNativeMediaType(MEDIA_FOUNDATION_FIRST_VIDEO_STREAM, index)
577567
} {
568+
index += 1;
578569
let fourcc = match unsafe { media_type.GetGUID(&MF_MT_SUBTYPE) } {
579570
Ok(fcc) => fcc,
580571
Err(why) => {
@@ -599,7 +590,7 @@ pub mod wmf {
599590
}
600591
};
601592

602-
// MFRatio is represented as 2 u32s in memory. This means we cann convert it to 2
593+
// MFRatio is represented as 2 u32s in memory. This means we can convert it to 2
603594
let framerate_list = {
604595
let mut framerates = vec![0_u32; 3];
605596
if let Ok(fraction_u64) =
@@ -612,16 +603,6 @@ pub mod wmf {
612603
}
613604
framerates.push(numerator);
614605
};
615-
if let Ok(fraction_u64) =
616-
unsafe { media_type.GetUINT64(&MF_MT_FRAME_RATE_RANGE_MAX) }
617-
{
618-
let mut numerator = (fraction_u64 >> 32) as u32;
619-
let denominator = fraction_u64 as u32;
620-
if denominator != 1 {
621-
numerator = 0;
622-
}
623-
framerates.push(numerator);
624-
};
625606
if let Ok(fraction_u64) = unsafe { media_type.GetUINT64(&MF_MT_FRAME_RATE) } {
626607
let mut numerator = (fraction_u64 >> 32) as u32;
627608
let denominator = fraction_u64 as u32;
@@ -660,8 +641,6 @@ pub mod wmf {
660641
));
661642
}
662643
}
663-
664-
index += 1;
665644
}
666645
Ok(camera_format_list)
667646
}
@@ -1016,88 +995,125 @@ pub mod wmf {
1016995
}
1017996

1018997
pub fn set_format(&mut self, format: CameraFormat) -> Result<(), NokhwaError> {
1019-
// convert to media_type
1020-
let media_type: IMFMediaType = match unsafe { MFCreateMediaType() } {
1021-
Ok(mt) => mt,
1022-
Err(why) => {
1023-
return Err(NokhwaError::StructureError {
1024-
structure: "IMFMediaType".to_string(),
1025-
error: why.to_string(),
1026-
})
998+
// We need to make sure to use all the original attributes of the IMFMediaType to avoid problems.
999+
// Otherwise, constructing IMFMediaType from scratch can sometimes fail due to not exactly matching.
1000+
// Therefore, we search for the first media_type that matches and also works correctly.
1001+
1002+
let mut last_error : Option<NokhwaError> = None;
1003+
1004+
let mut index = 0;
1005+
while let Ok(media_type) = unsafe {
1006+
self.source_reader
1007+
.GetNativeMediaType(MEDIA_FOUNDATION_FIRST_VIDEO_STREAM, index)
1008+
} {
1009+
index += 1;
1010+
let fourcc = match unsafe { media_type.GetGUID(&MF_MT_SUBTYPE) } {
1011+
Ok(fcc) => fcc,
1012+
Err(why) => {
1013+
return Err(NokhwaError::GetPropertyError {
1014+
property: "MF_MT_SUBTYPE".to_string(),
1015+
error: why.to_string(),
1016+
})
1017+
}
1018+
};
1019+
1020+
let frame_fmt = match guid_to_frameformat(fourcc) {
1021+
Some(fcc) => fcc,
1022+
None => continue,
1023+
};
1024+
1025+
if frame_fmt != format.format() {
1026+
continue;
10271027
}
1028-
};
10291028

1030-
// set relevant things
1031-
let resolution = (u64::from(format.resolution().width_x) << 32_u64)
1032-
+ u64::from(format.resolution().height_y);
1033-
let fps = {
1034-
let frame_rate_u64 = 0_u64;
1035-
let mut bytes: [u8; 8] = frame_rate_u64.to_le_bytes();
1036-
bytes[7] = format.frame_rate() as u8;
1037-
bytes[3] = 0x01;
1038-
u64::from_le_bytes(bytes)
1039-
};
1040-
let fourcc = frameformat_to_guid(format.format());
1041-
// setting to the new media_type
1042-
if let Err(why) = unsafe { media_type.SetGUID(&MF_MT_MAJOR_TYPE, &MFMediaType_Video) } {
1043-
return Err(NokhwaError::SetPropertyError {
1044-
property: "MF_MT_MAJOR_TYPE".to_string(),
1045-
value: "MFMediaType_Video".to_string(),
1046-
error: why.to_string(),
1047-
});
1048-
}
1049-
if let Err(why) = unsafe { media_type.SetGUID(&MF_MT_SUBTYPE, &fourcc) } {
1050-
return Err(NokhwaError::SetPropertyError {
1051-
property: "MF_MT_SUBTYPE".to_string(),
1052-
value: format!("{:?}", fourcc),
1053-
error: why.to_string(),
1054-
});
1055-
}
1056-
if let Err(why) = unsafe { media_type.SetUINT64(&MF_MT_FRAME_SIZE, resolution) } {
1057-
return Err(NokhwaError::SetPropertyError {
1058-
property: "MF_MT_FRAME_SIZE".to_string(),
1059-
value: resolution.to_string(),
1060-
error: why.to_string(),
1061-
});
1062-
}
1063-
if let Err(why) = unsafe { media_type.SetUINT64(&MF_MT_FRAME_RATE, fps) } {
1064-
return Err(NokhwaError::SetPropertyError {
1065-
property: "MF_MT_FRAME_RATE".to_string(),
1066-
value: fps.to_string(),
1067-
error: why.to_string(),
1068-
});
1069-
}
1070-
if let Err(why) = unsafe { media_type.SetUINT64(&MF_MT_FRAME_RATE_RANGE_MIN, fps) } {
1071-
return Err(NokhwaError::SetPropertyError {
1072-
property: "MF_MT_FRAME_RATE_RANGE_MIN".to_string(),
1073-
value: fps.to_string(),
1074-
error: why.to_string(),
1075-
});
1076-
}
1077-
if let Err(why) = unsafe { media_type.SetUINT64(&MF_MT_FRAME_RATE_RANGE_MAX, fps) } {
1078-
return Err(NokhwaError::SetPropertyError {
1079-
property: "MF_MT_FRAME_RATE_RANGE_MAX".to_string(),
1080-
value: fps.to_string(),
1081-
error: why.to_string(),
1082-
});
1029+
let (width, height) = match unsafe { media_type.GetUINT64(&MF_MT_FRAME_SIZE) } {
1030+
Ok(res_u64) => {
1031+
let width = (res_u64 >> 32) as u32;
1032+
let height = res_u64 as u32; // the cast will truncate the upper bits
1033+
(width, height)
1034+
}
1035+
Err(why) => {
1036+
return Err(NokhwaError::GetPropertyError {
1037+
property: "MF_MT_FRAME_SIZE".to_string(),
1038+
error: why.to_string(),
1039+
})
1040+
}
1041+
};
1042+
1043+
if (Resolution { width_x: width, height_y: height }) != format.resolution() {
1044+
continue;
1045+
}
1046+
1047+
// MFRatio is represented as 2 u32s in memory. This means we can convert it to 2
1048+
let framerate_list = {
1049+
let mut framerates = vec![0_u32; 3];
1050+
if let Ok(fraction_u64) =
1051+
unsafe { media_type.GetUINT64(&MF_MT_FRAME_RATE_RANGE_MAX) }
1052+
{
1053+
let mut numerator = (fraction_u64 >> 32) as u32;
1054+
let denominator = fraction_u64 as u32;
1055+
if denominator != 1 {
1056+
numerator = 0;
1057+
}
1058+
framerates.push(numerator);
1059+
};
1060+
if let Ok(fraction_u64) = unsafe { media_type.GetUINT64(&MF_MT_FRAME_RATE) } {
1061+
let mut numerator = (fraction_u64 >> 32) as u32;
1062+
let denominator = fraction_u64 as u32;
1063+
if denominator != 1 {
1064+
numerator = 0;
1065+
}
1066+
framerates.push(numerator);
1067+
};
1068+
if let Ok(fraction_u64) =
1069+
unsafe { media_type.GetUINT64(&MF_MT_FRAME_RATE_RANGE_MIN) }
1070+
{
1071+
let mut numerator = (fraction_u64 >> 32) as u32;
1072+
let denominator = fraction_u64 as u32;
1073+
if denominator != 1 {
1074+
numerator = 0;
1075+
}
1076+
framerates.push(numerator);
1077+
};
1078+
framerates
1079+
};
1080+
1081+
for frame_rate in framerate_list {
1082+
if frame_rate == format.frame_rate() {
1083+
let result = unsafe {
1084+
self.source_reader.SetCurrentMediaType(
1085+
MEDIA_FOUNDATION_FIRST_VIDEO_STREAM,
1086+
None,
1087+
&media_type,
1088+
)
1089+
};
1090+
1091+
match result {
1092+
Ok(_) => {
1093+
self.device_format = format;
1094+
self.format_refreshed()?;
1095+
return Ok(());
1096+
},
1097+
Err(why) => {
1098+
last_error = Some(NokhwaError::SetPropertyError {
1099+
property: "MEDIA_FOUNDATION_FIRST_VIDEO_STREAM".to_string(),
1100+
value: format!("{media_type:?}"),
1101+
error: why.to_string(),
1102+
});
1103+
}
1104+
}
1105+
}
1106+
}
10831107
}
10841108

1085-
if let Err(why) = unsafe {
1086-
self.source_reader.SetCurrentMediaType(
1087-
MEDIA_FOUNDATION_FIRST_VIDEO_STREAM,
1088-
None,
1089-
&media_type,
1090-
)
1091-
} {
1092-
return Err(NokhwaError::SetPropertyError {
1093-
property: "MEDIA_FOUNDATION_FIRST_VIDEO_STREAM".to_string(),
1094-
value: format!("{media_type:?}"),
1095-
error: why.to_string(),
1096-
});
1109+
if let Some(err) = last_error {
1110+
return Err(err);
10971111
}
1098-
self.device_format = format;
1099-
self.format_refreshed()?;
1100-
Ok(())
1112+
1113+
Err(NokhwaError::InitializeError {
1114+
backend: ApiBackend::MediaFoundation,
1115+
error: "Failed to fulfill requested format".to_string(),
1116+
})
11011117
}
11021118

11031119
pub fn is_stream_open(&self) -> bool {

0 commit comments

Comments
 (0)