diff --git a/crates/pf-encode/src/enc/windows/amf.rs b/crates/pf-encode/src/enc/windows/amf.rs index 4305fea9..ecfc28d1 100644 --- a/crates/pf-encode/src/enc/windows/amf.rs +++ b/crates/pf-encode/src/enc/windows/amf.rs @@ -66,425 +66,11 @@ use windows::Win32::Storage::FileSystem::{ }; use windows::Win32::System::LibraryLoader::GetModuleFileNameW; -// --------------------------------------------------------------------------------------------- -// Mirrored AMF C ABI (written against GPUOpen header release v1.4.36 — amf/public/include; every -// slot below is a base-interface slot whose layout is stable since <= v1.4.34, the loader's -// accepted ABI floor, so the mirror is valid on every runtime the loader admits). -// -// Layout rules this mirror relies on: every AMF interface is a struct whose sole member is a -// pointer to a C vtable; derived interfaces PREPEND their base's slots in order (AMFInterface → -// AMFPropertyStorage → AMFData → AMFBuffer/AMFSurface), so a derived pointer is usable through a -// base vtable mirror. Slots we never call are declared as bare `*const c_void` placeholders — -// same size/alignment as the function pointer they stand in for. `AMF_STD_CALL` is `__stdcall` -// (Rust `extern "system"`); the two DLL entry points are `__cdecl` (`extern "C"`); on x86_64 -// both collapse to the one Windows calling convention. -// --------------------------------------------------------------------------------------------- -mod sys { - use std::ffi::c_void; - - /// `AMF_RESULT` (core/Result.h) — a plain C enum, sequential from 0. Only the codes this - /// module branches on are named; everything else is reported numerically via [`result_name`]. - pub type AmfResult = i32; - pub const AMF_OK: AmfResult = 0; - pub const AMF_EOF: AmfResult = 23; - pub const AMF_REPEAT: AmfResult = 24; - pub const AMF_INPUT_FULL: AmfResult = 25; - pub const AMF_NEED_MORE_INPUT: AmfResult = 44; - - /// Human-readable name for an `AMF_RESULT` (diagnostics only — the numeric value rides along - /// so an unnamed code is still identifiable against Result.h). - pub fn result_name(r: AmfResult) -> &'static str { - match r { - 0 => "AMF_OK", - 1 => "AMF_FAIL", - 2 => "AMF_UNEXPECTED", - 3 => "AMF_ACCESS_DENIED", - 4 => "AMF_INVALID_ARG", - 5 => "AMF_OUT_OF_RANGE", - 6 => "AMF_OUT_OF_MEMORY", - 7 => "AMF_INVALID_POINTER", - 8 => "AMF_NO_INTERFACE", - 9 => "AMF_NOT_IMPLEMENTED", - 10 => "AMF_NOT_SUPPORTED", - 11 => "AMF_NOT_FOUND", - 12 => "AMF_ALREADY_INITIALIZED", - 13 => "AMF_NOT_INITIALIZED", - 14 => "AMF_INVALID_FORMAT", - 15 => "AMF_WRONG_STATE", - 17 => "AMF_NO_DEVICE", - 18 => "AMF_DIRECTX_FAILED", - 23 => "AMF_EOF", - 24 => "AMF_REPEAT", - 25 => "AMF_INPUT_FULL", - 26 => "AMF_RESOLUTION_CHANGED", - 28 => "AMF_INVALID_DATA_TYPE", - 29 => "AMF_INVALID_RESOLUTION", - 30 => "AMF_CODEC_NOT_SUPPORTED", - 31 => "AMF_SURFACE_FORMAT_NOT_SUPPORTED", - 32 => "AMF_SURFACE_MUST_BE_SHARED", - 36 => "AMF_ENCODER_NOT_PRESENT", - 44 => "AMF_NEED_MORE_INPUT", - _ => "AMF_", - } - } - - /// The AMF header release this FFI mirror was written against: `AMF_FULL_VERSION` for 1.4.36.0 - /// (core/Version.h `AMF_MAKE_FULL_VERSION`). This is the version claimed to `AMFInit` — but - /// capped at the runtime's own reported version (see `load_factory`), so an older-but-accepted - /// runtime is asked only for the ABI it actually provides. - pub const AMF_HEADER_VERSION: u64 = (1u64 << 48) | (4u64 << 32) | (36u64 << 16); - - /// The oldest AMF runtime the loader accepts (`AMF_FULL_VERSION` 1.4.34.0 — AMD Adrenalin - /// 24.6.1). This is an **ABI floor, not a feature floor**: every vtable slot mirrored in this - /// module belongs to a base interface (`AMFFactory`/`AMFContext`/`AMFComponent`/`AMFData`/ - /// `AMFBuffer`) whose layout has been stable — append-only, no mid-vtable insertions — since - /// well before 1.4.34, so a 1.4.34 runtime is guaranteed to expose every mirrored slot at its - /// mirrored offset. Everything 1.4.35/1.4.36 added that this path can touch (new HQ presets, - /// AV1 B-frame / picture management) is a *string-keyed encoder property*, applied through - /// [`set_prop`] with `required=false` — a runtime that lacks it rejects the property (logged) - /// and the feature degrades, rather than shifting any vtable offset. Below this floor the - /// mirror is not guaranteed to match, so the loader declines cleanly (an old-driver decline, - /// never UB). - pub const AMF_MIN_VERSION: u64 = (1u64 << 48) | (4u64 << 32) | (34u64 << 16); - - /// `AMF_SURFACE_FORMAT` (core/Surface.h). - pub const AMF_SURFACE_NV12: i32 = 1; - pub const AMF_SURFACE_P010: i32 = 10; - - /// `AMF_DX_VERSION::AMF_DX11_1` (core/Data.h) — the `InitDX11` version argument. - pub const AMF_DX11_1: i32 = 111; - - /// `AMF_MEMORY_TYPE::AMF_MEMORY_HOST` (core/Data.h) — the `AllocBuffer` memory type for the - /// CPU-filled HDR-metadata buffer. - pub const AMF_MEMORY_HOST: i32 = 1; - - /// `AMFHDRMetadata` (components/ColorSpace.h) — the payload of the `*InHDRMetadata` encoder - /// property (an `AMFBuffer` holding exactly this struct). Same units as the HEVC ST.2086 SEI - /// and [`punktfunk_core::quic::HdrMeta`]: chromaticities in 1/50000, mastering luminance in - /// 0.0001 cd/m², CLL/FALL in nits. 28 bytes, no padding. - #[repr(C)] - pub struct AmfHdrMetadata { - pub red_primary: [u16; 2], - pub green_primary: [u16; 2], - pub blue_primary: [u16; 2], - pub white_point: [u16; 2], - pub max_mastering_luminance: u32, - pub min_mastering_luminance: u32, - pub max_content_light_level: u16, - pub max_frame_average_light_level: u16, - } - - /// `AMFGuid` (core/Platform.h) — data41..data48 flattened into an array (identical layout). - #[repr(C)] - pub struct AmfGuid { - pub data1: u32, - pub data2: u16, - pub data3: u16, - pub data4: [u8; 8], - } - - /// `IID_AMFBuffer` (core/Buffer.h `AMF_DECLARE_IID`) — for `QueryInterface` on the encoder's - /// output `AMFData` to reach `GetNative`/`GetSize`. - pub const IID_AMF_BUFFER: AmfGuid = AmfGuid { - data1: 0xb04b_7248, - data2: 0xb6f0, - data3: 0x4321, - data4: [0xb6, 0x91, 0xba, 0xa4, 0x74, 0x0f, 0x9f, 0xcb], - }; - - // `AMF_VARIANT_TYPE` (core/Variant.h) — the tags this module writes/reads. - pub const AMF_VARIANT_BOOL: i32 = 1; - pub const AMF_VARIANT_INT64: i32 = 2; - pub const AMF_VARIANT_RATE: i32 = 7; - pub const AMF_VARIANT_INTERFACE: i32 = 12; - - /// `AMFVariantStruct` (core/Variant.h): a 4-byte C-enum tag + a 16-byte union whose largest - /// members are pointer/`amf_int64`/`AMFFloatVector4D` (align 8) → 24 bytes total, tag at 0, - /// payload at 8. Passed BY VALUE to `SetProperty` (Win64 passes >8-byte aggregates by hidden - /// reference on both sides, so declaring it by value matches the C compiler). The payload is - /// stored as two fully-initialised `u64`s — little-endian packing puts a bool in byte 0, an - /// `amf_int64` in word 0, and an `AMFRate{num,den}` as `num | den << 32`, exactly the union's - /// in-memory layout — so no partially-initialised union bytes ever cross the FFI. - #[repr(C)] - pub struct AmfVariant { - pub vtype: i32, - pub payload: [u64; 2], - } - - impl AmfVariant { - pub fn zeroed() -> Self { - AmfVariant { - vtype: 0, // AMF_VARIANT_EMPTY - payload: [0, 0], - } - } - pub fn from_i64(v: i64) -> Self { - AmfVariant { - vtype: AMF_VARIANT_INT64, - payload: [v as u64, 0], - } - } - pub fn from_bool(v: bool) -> Self { - AmfVariant { - vtype: AMF_VARIANT_BOOL, - payload: [v as u64, 0], - } - } - /// `AMFRate { num, den }` — two little-endian `amf_uint32`s in the union's first 8 bytes. - pub fn from_rate(num: u32, den: u32) -> Self { - AmfVariant { - vtype: AMF_VARIANT_RATE, - payload: [num as u64 | ((den as u64) << 32), 0], - } - } - /// An `AMFInterface*` payload (`pInterface` in the union's first 8 bytes). The property - /// storage AddRefs the interface when it copies the variant in (the C++ template - /// `SetProperty(name, AMFVariant(value))` passes a temporary whose destructor releases, - /// so `SetProperty` must take its own reference) — the caller keeps sole ownership of the - /// reference it already holds. - pub fn from_interface(p: *mut c_void) -> Self { - AmfVariant { - vtype: AMF_VARIANT_INTERFACE, - payload: [p as usize as u64, 0], - } - } - /// Read back an `amf_int64` payload (only valid when `vtype == AMF_VARIANT_INT64`). - pub fn as_i64(&self) -> Option { - (self.vtype == AMF_VARIANT_INT64).then_some(self.payload[0] as i64) - } - } - - /// Placeholder for a vtable slot this module never calls — same size/align as the function - /// pointer it stands in for, present only to keep the following slots at their C offsets. - pub type Slot = *const c_void; - - // -- AMFFactory (core/Factory.h; NOT refcounted — a process singleton) ---------------------- - #[repr(C)] - pub struct AmfFactory { - pub vtbl: *const AmfFactoryVtbl, - } - #[repr(C)] - pub struct AmfFactoryVtbl { - pub create_context: - unsafe extern "system" fn(*mut AmfFactory, *mut *mut AmfContext) -> AmfResult, - pub create_component: unsafe extern "system" fn( - *mut AmfFactory, - *mut AmfContext, - *const u16, - *mut *mut AmfComponent, - ) -> AmfResult, - pub set_cache_folder: Slot, - pub get_cache_folder: Slot, - pub get_debug: Slot, - pub get_trace: Slot, - pub get_programs: Slot, - } - - // -- AMFContext (core/Context.h) ------------------------------------------------------------ - #[repr(C)] - pub struct AmfContext { - pub vtbl: *const AmfContextVtbl, - } - #[repr(C)] - pub struct AmfContextVtbl { - // AMFInterface - pub acquire: Slot, - pub release: unsafe extern "system" fn(*mut AmfContext) -> i32, - pub query_interface: Slot, - // AMFPropertyStorage - pub set_property: Slot, - pub get_property: Slot, - pub has_property: Slot, - pub get_property_count: Slot, - pub get_property_at: Slot, - pub clear: Slot, - pub add_to: Slot, - pub copy_to: Slot, - pub add_observer: Slot, - pub remove_observer: Slot, - // AMFContext - pub terminate: unsafe extern "system" fn(*mut AmfContext) -> AmfResult, - pub init_dx9: Slot, - pub get_dx9_device: Slot, - pub lock_dx9: Slot, - pub unlock_dx9: Slot, - pub init_dx11: unsafe extern "system" fn(*mut AmfContext, *mut c_void, i32) -> AmfResult, - pub get_dx11_device: Slot, - pub lock_dx11: Slot, - pub unlock_dx11: Slot, - pub init_opencl: Slot, - pub get_opencl_context: Slot, - pub get_opencl_command_queue: Slot, - pub get_opencl_device_id: Slot, - pub get_opencl_compute_factory: Slot, - pub init_opencl_ex: Slot, - pub lock_opencl: Slot, - pub unlock_opencl: Slot, - pub init_opengl: Slot, - pub get_opengl_context: Slot, - pub get_opengl_drawable: Slot, - pub lock_opengl: Slot, - pub unlock_opengl: Slot, - pub init_xv: Slot, - pub get_xv_device: Slot, - pub lock_xv: Slot, - pub unlock_xv: Slot, - pub init_gralloc: Slot, - pub get_gralloc_device: Slot, - pub lock_gralloc: Slot, - pub unlock_gralloc: Slot, - pub alloc_buffer: unsafe extern "system" fn( - *mut AmfContext, - i32, // AMF_MEMORY_TYPE - usize, - *mut *mut AmfBuffer, - ) -> AmfResult, - pub alloc_surface: Slot, - pub alloc_audio_buffer: Slot, - pub create_buffer_from_host_native: Slot, - pub create_surface_from_host_native: Slot, - pub create_surface_from_dx9_native: Slot, - /// Out-param is `AMFSurface**` in the header; declared as the `AmfData` base here because - /// every surface call this module makes (`SetPts`, `SetProperty`, `Release`, - /// `SubmitInput`) lives in the `AMFData` vtable prefix, which `AMFSurfaceVtbl` reproduces - /// slot-for-slot (single inheritance, same object pointer). - pub create_surface_from_dx11_native: unsafe extern "system" fn( - *mut AmfContext, - *mut c_void, - *mut *mut AmfData, - *mut c_void, - ) -> AmfResult, - pub create_surface_from_opengl_native: Slot, - pub create_surface_from_gralloc_native: Slot, - pub create_surface_from_opencl_native: Slot, - pub create_buffer_from_opencl_native: Slot, - pub get_compute: Slot, - } - - // -- AMFComponent (components/Component.h) -------------------------------------------------- - #[repr(C)] - pub struct AmfComponent { - pub vtbl: *const AmfComponentVtbl, - } - #[repr(C)] - pub struct AmfComponentVtbl { - // AMFInterface - pub acquire: Slot, - pub release: unsafe extern "system" fn(*mut AmfComponent) -> i32, - pub query_interface: Slot, - // AMFPropertyStorage - pub set_property: - unsafe extern "system" fn(*mut AmfComponent, *const u16, AmfVariant) -> AmfResult, - pub get_property: Slot, - pub has_property: Slot, - pub get_property_count: Slot, - pub get_property_at: Slot, - pub clear: Slot, - pub add_to: Slot, - pub copy_to: Slot, - pub add_observer: Slot, - pub remove_observer: Slot, - // AMFPropertyStorageEx - pub get_properties_info_count: Slot, - pub get_property_info_at: Slot, - pub get_property_info: Slot, - pub validate_property: Slot, - // AMFComponent - pub init: unsafe extern "system" fn(*mut AmfComponent, i32, i32, i32) -> AmfResult, - pub reinit: Slot, - pub terminate: unsafe extern "system" fn(*mut AmfComponent) -> AmfResult, - pub drain: unsafe extern "system" fn(*mut AmfComponent) -> AmfResult, - pub flush: unsafe extern "system" fn(*mut AmfComponent) -> AmfResult, - pub submit_input: unsafe extern "system" fn(*mut AmfComponent, *mut AmfData) -> AmfResult, - pub query_output: - unsafe extern "system" fn(*mut AmfComponent, *mut *mut AmfData) -> AmfResult, - pub get_context: Slot, - pub set_output_data_allocator_cb: Slot, - pub get_caps: Slot, - pub optimize: Slot, - } - - // -- AMFData (core/Data.h) — also the usable prefix of AMFSurface -------------------------- - #[repr(C)] - pub struct AmfData { - pub vtbl: *const AmfDataVtbl, - } - #[repr(C)] - pub struct AmfDataVtbl { - // AMFInterface - pub acquire: Slot, - pub release: unsafe extern "system" fn(*mut AmfData) -> i32, - pub query_interface: - unsafe extern "system" fn(*mut AmfData, *const AmfGuid, *mut *mut c_void) -> AmfResult, - // AMFPropertyStorage - pub set_property: - unsafe extern "system" fn(*mut AmfData, *const u16, AmfVariant) -> AmfResult, - pub get_property: - unsafe extern "system" fn(*mut AmfData, *const u16, *mut AmfVariant) -> AmfResult, - pub has_property: Slot, - pub get_property_count: Slot, - pub get_property_at: Slot, - pub clear: Slot, - pub add_to: Slot, - pub copy_to: Slot, - pub add_observer: Slot, - pub remove_observer: Slot, - // AMFData - pub get_memory_type: Slot, - pub duplicate: Slot, - pub convert: Slot, - pub interop: Slot, - pub get_data_type: Slot, - pub is_reusable: Slot, - pub set_pts: unsafe extern "system" fn(*mut AmfData, i64), - pub get_pts: Slot, - pub set_duration: Slot, - pub get_duration: Slot, - } - - // -- AMFBuffer (core/Buffer.h) — the encoder's output object ------------------------------- - #[repr(C)] - pub struct AmfBuffer { - pub vtbl: *const AmfBufferVtbl, - } - #[repr(C)] - pub struct AmfBufferVtbl { - // AMFInterface + AMFPropertyStorage + AMFData prefix (identical order to AmfDataVtbl). - pub acquire: Slot, - pub release: unsafe extern "system" fn(*mut AmfBuffer) -> i32, - pub query_interface: Slot, - pub set_property: Slot, - pub get_property: Slot, - pub has_property: Slot, - pub get_property_count: Slot, - pub get_property_at: Slot, - pub clear: Slot, - pub add_to: Slot, - pub copy_to: Slot, - pub add_observer: Slot, - pub remove_observer: Slot, - pub get_memory_type: Slot, - pub duplicate: Slot, - pub convert: Slot, - pub interop: Slot, - pub get_data_type: Slot, - pub is_reusable: Slot, - pub set_pts: Slot, - pub get_pts: Slot, - pub set_duration: Slot, - pub get_duration: Slot, - // AMFBuffer - pub set_size: Slot, - pub get_size: unsafe extern "system" fn(*mut AmfBuffer) -> usize, - pub get_native: unsafe extern "system" fn(*mut AmfBuffer) -> *mut c_void, - pub add_observer_buffer: Slot, - pub remove_observer_buffer: Slot, - } - - // -- DLL entry points (core/Factory.h; AMF_CDECL_CALL) -------------------------------------- - pub type AmfQueryVersionFn = unsafe extern "C" fn(*mut u64) -> AmfResult; - pub type AmfInitFn = unsafe extern "C" fn(u64, *mut *mut AmfFactory) -> AmfResult; -} +// The hand-mirrored AMF C ABI lives in `amf_sys.rs` (WP7.4): pure FFI surface, no policy — +// isolating the unsafe vtable mirror from the encoder logic is the crate's stated review +// goal, and the `#[path]` keeps the module name (`sys::`) and every call site unchanged. +#[path = "amf_sys.rs"] +mod sys; use sys::{result_name, AmfVariant}; diff --git a/crates/pf-encode/src/enc/windows/amf_sys.rs b/crates/pf-encode/src/enc/windows/amf_sys.rs new file mode 100644 index 00000000..0a5e5a68 --- /dev/null +++ b/crates/pf-encode/src/enc/windows/amf_sys.rs @@ -0,0 +1,414 @@ +//! Mirrored AMF C ABI (written against GPUOpen header release v1.4.36 — amf/public/include; every +//! slot below is a base-interface slot whose layout is stable since <= v1.4.34, the loader's +//! accepted ABI floor, so the mirror is valid on every runtime the loader admits). +//! +//! Layout rules this mirror relies on: every AMF interface is a struct whose sole member is a +//! pointer to a C vtable; derived interfaces PREPEND their base's slots in order (AMFInterface → +//! AMFPropertyStorage → AMFData → AMFBuffer/AMFSurface), so a derived pointer is usable through a +//! base vtable mirror. Slots we never call are declared as bare `*const c_void` placeholders — +//! same size/alignment as the function pointer they stand in for. `AMF_STD_CALL` is `__stdcall` +//! (Rust `extern "system"`); the two DLL entry points are `__cdecl` (`extern "C"`); on x86_64 +//! both collapse to the one Windows calling convention. + +use std::ffi::c_void; + +/// `AMF_RESULT` (core/Result.h) — a plain C enum, sequential from 0. Only the codes this +/// module branches on are named; everything else is reported numerically via [`result_name`]. +pub type AmfResult = i32; +pub const AMF_OK: AmfResult = 0; +pub const AMF_EOF: AmfResult = 23; +pub const AMF_REPEAT: AmfResult = 24; +pub const AMF_INPUT_FULL: AmfResult = 25; +pub const AMF_NEED_MORE_INPUT: AmfResult = 44; + +/// Human-readable name for an `AMF_RESULT` (diagnostics only — the numeric value rides along +/// so an unnamed code is still identifiable against Result.h). +pub fn result_name(r: AmfResult) -> &'static str { + match r { + 0 => "AMF_OK", + 1 => "AMF_FAIL", + 2 => "AMF_UNEXPECTED", + 3 => "AMF_ACCESS_DENIED", + 4 => "AMF_INVALID_ARG", + 5 => "AMF_OUT_OF_RANGE", + 6 => "AMF_OUT_OF_MEMORY", + 7 => "AMF_INVALID_POINTER", + 8 => "AMF_NO_INTERFACE", + 9 => "AMF_NOT_IMPLEMENTED", + 10 => "AMF_NOT_SUPPORTED", + 11 => "AMF_NOT_FOUND", + 12 => "AMF_ALREADY_INITIALIZED", + 13 => "AMF_NOT_INITIALIZED", + 14 => "AMF_INVALID_FORMAT", + 15 => "AMF_WRONG_STATE", + 17 => "AMF_NO_DEVICE", + 18 => "AMF_DIRECTX_FAILED", + 23 => "AMF_EOF", + 24 => "AMF_REPEAT", + 25 => "AMF_INPUT_FULL", + 26 => "AMF_RESOLUTION_CHANGED", + 28 => "AMF_INVALID_DATA_TYPE", + 29 => "AMF_INVALID_RESOLUTION", + 30 => "AMF_CODEC_NOT_SUPPORTED", + 31 => "AMF_SURFACE_FORMAT_NOT_SUPPORTED", + 32 => "AMF_SURFACE_MUST_BE_SHARED", + 36 => "AMF_ENCODER_NOT_PRESENT", + 44 => "AMF_NEED_MORE_INPUT", + _ => "AMF_", + } +} + +/// The AMF header release this FFI mirror was written against: `AMF_FULL_VERSION` for 1.4.36.0 +/// (core/Version.h `AMF_MAKE_FULL_VERSION`). This is the version claimed to `AMFInit` — but +/// capped at the runtime's own reported version (see `load_factory`), so an older-but-accepted +/// runtime is asked only for the ABI it actually provides. +pub const AMF_HEADER_VERSION: u64 = (1u64 << 48) | (4u64 << 32) | (36u64 << 16); + +/// The oldest AMF runtime the loader accepts (`AMF_FULL_VERSION` 1.4.34.0 — AMD Adrenalin +/// 24.6.1). This is an **ABI floor, not a feature floor**: every vtable slot mirrored in this +/// module belongs to a base interface (`AMFFactory`/`AMFContext`/`AMFComponent`/`AMFData`/ +/// `AMFBuffer`) whose layout has been stable — append-only, no mid-vtable insertions — since +/// well before 1.4.34, so a 1.4.34 runtime is guaranteed to expose every mirrored slot at its +/// mirrored offset. Everything 1.4.35/1.4.36 added that this path can touch (new HQ presets, +/// AV1 B-frame / picture management) is a *string-keyed encoder property*, applied through +/// [`set_prop`] with `required=false` — a runtime that lacks it rejects the property (logged) +/// and the feature degrades, rather than shifting any vtable offset. Below this floor the +/// mirror is not guaranteed to match, so the loader declines cleanly (an old-driver decline, +/// never UB). +pub const AMF_MIN_VERSION: u64 = (1u64 << 48) | (4u64 << 32) | (34u64 << 16); + +/// `AMF_SURFACE_FORMAT` (core/Surface.h). +pub const AMF_SURFACE_NV12: i32 = 1; +pub const AMF_SURFACE_P010: i32 = 10; + +/// `AMF_DX_VERSION::AMF_DX11_1` (core/Data.h) — the `InitDX11` version argument. +pub const AMF_DX11_1: i32 = 111; + +/// `AMF_MEMORY_TYPE::AMF_MEMORY_HOST` (core/Data.h) — the `AllocBuffer` memory type for the +/// CPU-filled HDR-metadata buffer. +pub const AMF_MEMORY_HOST: i32 = 1; + +/// `AMFHDRMetadata` (components/ColorSpace.h) — the payload of the `*InHDRMetadata` encoder +/// property (an `AMFBuffer` holding exactly this struct). Same units as the HEVC ST.2086 SEI +/// and [`punktfunk_core::quic::HdrMeta`]: chromaticities in 1/50000, mastering luminance in +/// 0.0001 cd/m², CLL/FALL in nits. 28 bytes, no padding. +#[repr(C)] +pub struct AmfHdrMetadata { + pub red_primary: [u16; 2], + pub green_primary: [u16; 2], + pub blue_primary: [u16; 2], + pub white_point: [u16; 2], + pub max_mastering_luminance: u32, + pub min_mastering_luminance: u32, + pub max_content_light_level: u16, + pub max_frame_average_light_level: u16, +} + +/// `AMFGuid` (core/Platform.h) — data41..data48 flattened into an array (identical layout). +#[repr(C)] +pub struct AmfGuid { + pub data1: u32, + pub data2: u16, + pub data3: u16, + pub data4: [u8; 8], +} + +/// `IID_AMFBuffer` (core/Buffer.h `AMF_DECLARE_IID`) — for `QueryInterface` on the encoder's +/// output `AMFData` to reach `GetNative`/`GetSize`. +pub const IID_AMF_BUFFER: AmfGuid = AmfGuid { + data1: 0xb04b_7248, + data2: 0xb6f0, + data3: 0x4321, + data4: [0xb6, 0x91, 0xba, 0xa4, 0x74, 0x0f, 0x9f, 0xcb], +}; + +// `AMF_VARIANT_TYPE` (core/Variant.h) — the tags this module writes/reads. +pub const AMF_VARIANT_BOOL: i32 = 1; +pub const AMF_VARIANT_INT64: i32 = 2; +pub const AMF_VARIANT_RATE: i32 = 7; +pub const AMF_VARIANT_INTERFACE: i32 = 12; + +/// `AMFVariantStruct` (core/Variant.h): a 4-byte C-enum tag + a 16-byte union whose largest +/// members are pointer/`amf_int64`/`AMFFloatVector4D` (align 8) → 24 bytes total, tag at 0, +/// payload at 8. Passed BY VALUE to `SetProperty` (Win64 passes >8-byte aggregates by hidden +/// reference on both sides, so declaring it by value matches the C compiler). The payload is +/// stored as two fully-initialised `u64`s — little-endian packing puts a bool in byte 0, an +/// `amf_int64` in word 0, and an `AMFRate{num,den}` as `num | den << 32`, exactly the union's +/// in-memory layout — so no partially-initialised union bytes ever cross the FFI. +#[repr(C)] +pub struct AmfVariant { + pub vtype: i32, + pub payload: [u64; 2], +} + +impl AmfVariant { + pub fn zeroed() -> Self { + AmfVariant { + vtype: 0, // AMF_VARIANT_EMPTY + payload: [0, 0], + } + } + pub fn from_i64(v: i64) -> Self { + AmfVariant { + vtype: AMF_VARIANT_INT64, + payload: [v as u64, 0], + } + } + pub fn from_bool(v: bool) -> Self { + AmfVariant { + vtype: AMF_VARIANT_BOOL, + payload: [v as u64, 0], + } + } + /// `AMFRate { num, den }` — two little-endian `amf_uint32`s in the union's first 8 bytes. + pub fn from_rate(num: u32, den: u32) -> Self { + AmfVariant { + vtype: AMF_VARIANT_RATE, + payload: [num as u64 | ((den as u64) << 32), 0], + } + } + /// An `AMFInterface*` payload (`pInterface` in the union's first 8 bytes). The property + /// storage AddRefs the interface when it copies the variant in (the C++ template + /// `SetProperty(name, AMFVariant(value))` passes a temporary whose destructor releases, + /// so `SetProperty` must take its own reference) — the caller keeps sole ownership of the + /// reference it already holds. + pub fn from_interface(p: *mut c_void) -> Self { + AmfVariant { + vtype: AMF_VARIANT_INTERFACE, + payload: [p as usize as u64, 0], + } + } + /// Read back an `amf_int64` payload (only valid when `vtype == AMF_VARIANT_INT64`). + pub fn as_i64(&self) -> Option { + (self.vtype == AMF_VARIANT_INT64).then_some(self.payload[0] as i64) + } +} + +/// Placeholder for a vtable slot this module never calls — same size/align as the function +/// pointer it stands in for, present only to keep the following slots at their C offsets. +pub type Slot = *const c_void; + +// -- AMFFactory (core/Factory.h; NOT refcounted — a process singleton) ---------------------- +#[repr(C)] +pub struct AmfFactory { + pub vtbl: *const AmfFactoryVtbl, +} +#[repr(C)] +pub struct AmfFactoryVtbl { + pub create_context: + unsafe extern "system" fn(*mut AmfFactory, *mut *mut AmfContext) -> AmfResult, + pub create_component: unsafe extern "system" fn( + *mut AmfFactory, + *mut AmfContext, + *const u16, + *mut *mut AmfComponent, + ) -> AmfResult, + pub set_cache_folder: Slot, + pub get_cache_folder: Slot, + pub get_debug: Slot, + pub get_trace: Slot, + pub get_programs: Slot, +} + +// -- AMFContext (core/Context.h) ------------------------------------------------------------ +#[repr(C)] +pub struct AmfContext { + pub vtbl: *const AmfContextVtbl, +} +#[repr(C)] +pub struct AmfContextVtbl { + // AMFInterface + pub acquire: Slot, + pub release: unsafe extern "system" fn(*mut AmfContext) -> i32, + pub query_interface: Slot, + // AMFPropertyStorage + pub set_property: Slot, + pub get_property: Slot, + pub has_property: Slot, + pub get_property_count: Slot, + pub get_property_at: Slot, + pub clear: Slot, + pub add_to: Slot, + pub copy_to: Slot, + pub add_observer: Slot, + pub remove_observer: Slot, + // AMFContext + pub terminate: unsafe extern "system" fn(*mut AmfContext) -> AmfResult, + pub init_dx9: Slot, + pub get_dx9_device: Slot, + pub lock_dx9: Slot, + pub unlock_dx9: Slot, + pub init_dx11: unsafe extern "system" fn(*mut AmfContext, *mut c_void, i32) -> AmfResult, + pub get_dx11_device: Slot, + pub lock_dx11: Slot, + pub unlock_dx11: Slot, + pub init_opencl: Slot, + pub get_opencl_context: Slot, + pub get_opencl_command_queue: Slot, + pub get_opencl_device_id: Slot, + pub get_opencl_compute_factory: Slot, + pub init_opencl_ex: Slot, + pub lock_opencl: Slot, + pub unlock_opencl: Slot, + pub init_opengl: Slot, + pub get_opengl_context: Slot, + pub get_opengl_drawable: Slot, + pub lock_opengl: Slot, + pub unlock_opengl: Slot, + pub init_xv: Slot, + pub get_xv_device: Slot, + pub lock_xv: Slot, + pub unlock_xv: Slot, + pub init_gralloc: Slot, + pub get_gralloc_device: Slot, + pub lock_gralloc: Slot, + pub unlock_gralloc: Slot, + pub alloc_buffer: unsafe extern "system" fn( + *mut AmfContext, + i32, // AMF_MEMORY_TYPE + usize, + *mut *mut AmfBuffer, + ) -> AmfResult, + pub alloc_surface: Slot, + pub alloc_audio_buffer: Slot, + pub create_buffer_from_host_native: Slot, + pub create_surface_from_host_native: Slot, + pub create_surface_from_dx9_native: Slot, + /// Out-param is `AMFSurface**` in the header; declared as the `AmfData` base here because + /// every surface call this module makes (`SetPts`, `SetProperty`, `Release`, + /// `SubmitInput`) lives in the `AMFData` vtable prefix, which `AMFSurfaceVtbl` reproduces + /// slot-for-slot (single inheritance, same object pointer). + pub create_surface_from_dx11_native: unsafe extern "system" fn( + *mut AmfContext, + *mut c_void, + *mut *mut AmfData, + *mut c_void, + ) -> AmfResult, + pub create_surface_from_opengl_native: Slot, + pub create_surface_from_gralloc_native: Slot, + pub create_surface_from_opencl_native: Slot, + pub create_buffer_from_opencl_native: Slot, + pub get_compute: Slot, +} + +// -- AMFComponent (components/Component.h) -------------------------------------------------- +#[repr(C)] +pub struct AmfComponent { + pub vtbl: *const AmfComponentVtbl, +} +#[repr(C)] +pub struct AmfComponentVtbl { + // AMFInterface + pub acquire: Slot, + pub release: unsafe extern "system" fn(*mut AmfComponent) -> i32, + pub query_interface: Slot, + // AMFPropertyStorage + pub set_property: + unsafe extern "system" fn(*mut AmfComponent, *const u16, AmfVariant) -> AmfResult, + pub get_property: Slot, + pub has_property: Slot, + pub get_property_count: Slot, + pub get_property_at: Slot, + pub clear: Slot, + pub add_to: Slot, + pub copy_to: Slot, + pub add_observer: Slot, + pub remove_observer: Slot, + // AMFPropertyStorageEx + pub get_properties_info_count: Slot, + pub get_property_info_at: Slot, + pub get_property_info: Slot, + pub validate_property: Slot, + // AMFComponent + pub init: unsafe extern "system" fn(*mut AmfComponent, i32, i32, i32) -> AmfResult, + pub reinit: Slot, + pub terminate: unsafe extern "system" fn(*mut AmfComponent) -> AmfResult, + pub drain: unsafe extern "system" fn(*mut AmfComponent) -> AmfResult, + pub flush: unsafe extern "system" fn(*mut AmfComponent) -> AmfResult, + pub submit_input: unsafe extern "system" fn(*mut AmfComponent, *mut AmfData) -> AmfResult, + pub query_output: unsafe extern "system" fn(*mut AmfComponent, *mut *mut AmfData) -> AmfResult, + pub get_context: Slot, + pub set_output_data_allocator_cb: Slot, + pub get_caps: Slot, + pub optimize: Slot, +} + +// -- AMFData (core/Data.h) — also the usable prefix of AMFSurface -------------------------- +#[repr(C)] +pub struct AmfData { + pub vtbl: *const AmfDataVtbl, +} +#[repr(C)] +pub struct AmfDataVtbl { + // AMFInterface + pub acquire: Slot, + pub release: unsafe extern "system" fn(*mut AmfData) -> i32, + pub query_interface: + unsafe extern "system" fn(*mut AmfData, *const AmfGuid, *mut *mut c_void) -> AmfResult, + // AMFPropertyStorage + pub set_property: unsafe extern "system" fn(*mut AmfData, *const u16, AmfVariant) -> AmfResult, + pub get_property: + unsafe extern "system" fn(*mut AmfData, *const u16, *mut AmfVariant) -> AmfResult, + pub has_property: Slot, + pub get_property_count: Slot, + pub get_property_at: Slot, + pub clear: Slot, + pub add_to: Slot, + pub copy_to: Slot, + pub add_observer: Slot, + pub remove_observer: Slot, + // AMFData + pub get_memory_type: Slot, + pub duplicate: Slot, + pub convert: Slot, + pub interop: Slot, + pub get_data_type: Slot, + pub is_reusable: Slot, + pub set_pts: unsafe extern "system" fn(*mut AmfData, i64), + pub get_pts: Slot, + pub set_duration: Slot, + pub get_duration: Slot, +} + +// -- AMFBuffer (core/Buffer.h) — the encoder's output object ------------------------------- +#[repr(C)] +pub struct AmfBuffer { + pub vtbl: *const AmfBufferVtbl, +} +#[repr(C)] +pub struct AmfBufferVtbl { + // AMFInterface + AMFPropertyStorage + AMFData prefix (identical order to AmfDataVtbl). + pub acquire: Slot, + pub release: unsafe extern "system" fn(*mut AmfBuffer) -> i32, + pub query_interface: Slot, + pub set_property: Slot, + pub get_property: Slot, + pub has_property: Slot, + pub get_property_count: Slot, + pub get_property_at: Slot, + pub clear: Slot, + pub add_to: Slot, + pub copy_to: Slot, + pub add_observer: Slot, + pub remove_observer: Slot, + pub get_memory_type: Slot, + pub duplicate: Slot, + pub convert: Slot, + pub interop: Slot, + pub get_data_type: Slot, + pub is_reusable: Slot, + pub set_pts: Slot, + pub get_pts: Slot, + pub set_duration: Slot, + pub get_duration: Slot, + // AMFBuffer + pub set_size: Slot, + pub get_size: unsafe extern "system" fn(*mut AmfBuffer) -> usize, + pub get_native: unsafe extern "system" fn(*mut AmfBuffer) -> *mut c_void, + pub add_observer_buffer: Slot, + pub remove_observer_buffer: Slot, +} + +// -- DLL entry points (core/Factory.h; AMF_CDECL_CALL) -------------------------------------- +pub type AmfQueryVersionFn = unsafe extern "C" fn(*mut u64) -> AmfResult; +pub type AmfInitFn = unsafe extern "C" fn(u64, *mut *mut AmfFactory) -> AmfResult;