#![cfg(test)]
#![allow(
clippy::unwrap_used,
clippy::expect_used,
clippy::print_stderr,
reason = "unit tests"
)]
use windows::Win32::Media::MediaFoundation::{
IMFActivate, MFMediaType_Video, MFT_CATEGORY_VIDEO_ENCODER, MFT_ENUM_FLAG,
MFT_ENUM_FLAG_HARDWARE, MFT_ENUM_FLAG_SORTANDFILTER, MFT_FRIENDLY_NAME_Attribute,
MFT_REGISTER_TYPE_INFO, MFTEnumEx, MFVideoFormat_AV1, MFVideoFormat_HEVC, MFVideoFormat_VP90,
};
use windows::Win32::System::Com::CoTaskMemFree;
use windows::core::PWSTR;
#[test]
fn list_encoder_mfts_for_each_codec() {
super::super::runtime::ensure_mf().expect("MF runtime init");
for (name, subtype) in [
("HEVC", MFVideoFormat_HEVC),
("AV1", MFVideoFormat_AV1),
("VP9", MFVideoFormat_VP90),
] {
let unfiltered = enum_encoder_mft_names(subtype, false);
let hw_only = enum_encoder_mft_names(subtype, true);
eprintln!("{name}: any-flag encoder MFTs = {unfiltered:?}");
eprintln!("{name}: MFT_ENUM_FLAG_HARDWARE encoder MFTs = {hw_only:?}");
}
}
fn enum_encoder_mft_names(subtype: windows::core::GUID, hardware_only: bool) -> Vec<String> {
let output = MFT_REGISTER_TYPE_INFO {
guidMajorType: MFMediaType_Video,
guidSubtype: subtype,
};
let flags = if hardware_only {
MFT_ENUM_FLAG(MFT_ENUM_FLAG_HARDWARE.0 | MFT_ENUM_FLAG_SORTANDFILTER.0)
} else {
MFT_ENUM_FLAG_SORTANDFILTER
};
let mut activates: *mut Option<IMFActivate> = std::ptr::null_mut();
let mut count = 0u32;
let hr = unsafe {
MFTEnumEx(
MFT_CATEGORY_VIDEO_ENCODER,
flags,
None,
Some(std::ptr::from_ref(&output)),
&raw mut activates,
&raw mut count,
)
};
if hr.is_err() || activates.is_null() {
return Vec::new();
}
let mut names = Vec::new();
for i in 0..count as usize {
let activate = unsafe { (*activates.add(i)).take() };
if let Some(activate) = activate {
names.push(friendly_name(&activate).unwrap_or_else(|| "<unnamed>".to_owned()));
}
}
unsafe {
CoTaskMemFree(Some(activates.cast_const().cast()));
}
names
}
fn friendly_name(activate: &IMFActivate) -> Option<String> {
let mut raw = PWSTR::null();
let mut len = 0u32;
unsafe {
activate.GetAllocatedString(&MFT_FRIENDLY_NAME_Attribute, &raw mut raw, &raw mut len)
}
.ok()?;
if raw.is_null() {
return None;
}
let name = unsafe { raw.to_string() }.ok();
unsafe {
CoTaskMemFree(Some(raw.0.cast()));
}
name
}