#![allow(unsafe_code)]
use crate::DecodeError;
use mediaway_common::{Bytes, Packet};
use windows::Win32::Media::MediaFoundation::{
IMFMediaBuffer, IMFSample, IMFTransform, MF_E_TRANSFORM_NEED_MORE_INPUT,
MF_E_TRANSFORM_STREAM_CHANGE, MF_MT_FRAME_SIZE, MF_MT_INTERLACE_MODE, MF_MT_MAJOR_TYPE,
MF_MT_MPEG_SEQUENCE_HEADER, MF_MT_SUBTYPE, MFCreateMediaType, MFCreateMemoryBuffer,
MFCreateSample, MFMediaType_Video, MFT_MESSAGE_NOTIFY_BEGIN_STREAMING,
MFT_MESSAGE_NOTIFY_END_STREAMING, MFT_MESSAGE_NOTIFY_START_OF_STREAM, MFT_OUTPUT_DATA_BUFFER,
MFVideoFormat_NV12, MFVideoInterlace_Progressive,
};
use windows::core::GUID;
use super::runtime::{pack_u32_pair, unpack_u32_pair};
pub(super) enum Drain {
Sample(IMFSample),
NeedMore,
StreamChange,
}
pub(super) fn configure_decode_types(
transform: &IMFTransform,
width: u32,
height: u32,
extra_data: &Bytes,
input_subtype: &GUID,
) -> Result<(), DecodeError> {
let in_type = unsafe { MFCreateMediaType() }.map_err(|_| DecodeError::Backend)?;
unsafe {
in_type
.SetGUID(&MF_MT_MAJOR_TYPE, &MFMediaType_Video)
.map_err(|_| DecodeError::Backend)?;
in_type
.SetGUID(&MF_MT_SUBTYPE, input_subtype)
.map_err(|_| DecodeError::Backend)?;
if width > 0 && height > 0 {
in_type
.SetUINT64(&MF_MT_FRAME_SIZE, pack_u32_pair(width, height))
.map_err(|_| DecodeError::Backend)?;
}
in_type
.SetUINT32(&MF_MT_INTERLACE_MODE, MFVideoInterlace_Progressive.0 as u32)
.map_err(|_| DecodeError::Backend)?;
if !extra_data.is_empty() {
in_type
.SetBlob(&MF_MT_MPEG_SEQUENCE_HEADER, extra_data.as_ref())
.map_err(|_| DecodeError::Backend)?;
}
transform
.SetInputType(0, &in_type, 0)
.map_err(|_| DecodeError::Backend)?;
}
let out_type = unsafe { MFCreateMediaType() }.map_err(|_| DecodeError::Backend)?;
unsafe {
out_type
.SetGUID(&MF_MT_MAJOR_TYPE, &MFMediaType_Video)
.map_err(|_| DecodeError::Backend)?;
out_type
.SetGUID(&MF_MT_SUBTYPE, &MFVideoFormat_NV12)
.map_err(|_| DecodeError::Backend)?;
if width > 0 && height > 0 {
out_type
.SetUINT64(&MF_MT_FRAME_SIZE, pack_u32_pair(width, height))
.map_err(|_| DecodeError::Backend)?;
}
out_type
.SetUINT32(&MF_MT_INTERLACE_MODE, MFVideoInterlace_Progressive.0 as u32)
.map_err(|_| DecodeError::Backend)?;
transform
.SetOutputType(0, &out_type, 0)
.map_err(|_| DecodeError::Backend)?;
}
Ok(())
}
pub(super) fn begin_streaming(transform: &IMFTransform) -> Result<(), DecodeError> {
unsafe {
transform
.ProcessMessage(MFT_MESSAGE_NOTIFY_BEGIN_STREAMING, 0)
.map_err(|_| DecodeError::Backend)?;
transform
.ProcessMessage(MFT_MESSAGE_NOTIFY_START_OF_STREAM, 0)
.map_err(|_| DecodeError::Backend)?;
}
Ok(())
}
pub(super) fn process_one_output(
transform: &IMFTransform,
output_provides_samples: bool,
output_buf_size: u32,
) -> Result<Drain, DecodeError> {
let mut status = 0u32;
let mut buffers = if output_provides_samples {
[MFT_OUTPUT_DATA_BUFFER {
dwStreamID: 0,
pSample: std::mem::ManuallyDrop::new(None),
dwStatus: 0,
pEvents: std::mem::ManuallyDrop::new(None),
}]
} else {
let out_sample: IMFSample =
unsafe { MFCreateSample() }.map_err(|_| DecodeError::Backend)?;
let out_buffer =
unsafe { MFCreateMemoryBuffer(output_buf_size) }.map_err(|_| DecodeError::Backend)?;
unsafe { out_sample.AddBuffer(&out_buffer) }.map_err(|_| DecodeError::Backend)?;
[MFT_OUTPUT_DATA_BUFFER {
dwStreamID: 0,
pSample: std::mem::ManuallyDrop::new(Some(out_sample)),
dwStatus: 0,
pEvents: std::mem::ManuallyDrop::new(None),
}]
};
let hr = unsafe { transform.ProcessOutput(0, &mut buffers, &raw mut status) };
let sample = unsafe { std::mem::ManuallyDrop::take(&mut buffers[0].pSample) };
let _ = unsafe { std::mem::ManuallyDrop::take(&mut buffers[0].pEvents) };
if let Err(e) = hr {
if e.code() == MF_E_TRANSFORM_NEED_MORE_INPUT {
return Ok(Drain::NeedMore);
}
if e.code() == MF_E_TRANSFORM_STREAM_CHANGE {
return Ok(Drain::StreamChange);
}
return Err(DecodeError::Backend);
}
let Some(sample) = sample else {
return Ok(Drain::NeedMore);
};
Ok(Drain::Sample(sample))
}
pub(super) fn packet_to_sample(
packet: &Packet,
time_base_num: u64,
time_base_den: u32,
nal_length_size: Option<u8>,
) -> Result<IMFSample, DecodeError> {
if packet.payload.is_empty() {
return Err(DecodeError::InvalidInput);
}
let annex_b_payload = nal_length_size.map_or_else(
|| packet.payload.clone(), |n| iso_bmff::bitstream::avc::avcc_payload_to_annex_b(&packet.payload, n),
);
if annex_b_payload.is_empty() {
return Err(DecodeError::InvalidInput);
}
let len = u32::try_from(annex_b_payload.len()).map_err(|_| DecodeError::InvalidInput)?;
let sample: IMFSample = unsafe { MFCreateSample() }.map_err(|_| DecodeError::Backend)?;
let buffer: IMFMediaBuffer =
unsafe { MFCreateMemoryBuffer(len) }.map_err(|_| DecodeError::Backend)?;
unsafe {
let mut ptr = std::ptr::null_mut();
let mut max_len = 0u32;
let mut cur_len = 0u32;
buffer
.Lock(
&raw mut ptr,
Some(std::ptr::from_mut(&mut max_len)),
Some(std::ptr::from_mut(&mut cur_len)),
)
.map_err(|_| DecodeError::Backend)?;
if ptr.is_null() || max_len < len {
let _: windows::core::Result<()> = buffer.Unlock();
return Err(DecodeError::Backend);
}
std::ptr::copy_nonoverlapping(annex_b_payload.as_ptr(), ptr, annex_b_payload.len());
buffer
.SetCurrentLength(len)
.map_err(|_| DecodeError::Backend)?;
buffer.Unlock().map_err(|_| DecodeError::Backend)?;
}
unsafe { sample.AddBuffer(&buffer) }.map_err(|_| DecodeError::Backend)?;
let hns = super::runtime::to_hns(packet.pts, time_base_num, time_base_den);
let dur = super::runtime::to_hns(
i64::try_from(packet.duration).unwrap_or(0),
time_base_num,
time_base_den,
)
.max(1);
unsafe {
sample
.SetSampleTime(hns)
.map_err(|_| DecodeError::Backend)?;
sample
.SetSampleDuration(dur)
.map_err(|_| DecodeError::Backend)?;
}
Ok(sample)
}
pub(super) fn output_buffer_size(transform: &IMFTransform) -> Result<u32, DecodeError> {
let out_info = unsafe { transform.GetOutputStreamInfo(0) }.map_err(|_| DecodeError::Backend)?;
Ok(out_info.cbSize.max(1))
}
pub(super) fn read_output_dimensions(transform: &IMFTransform) -> Result<(u32, u32), DecodeError> {
let mt = unsafe { transform.GetOutputCurrentType(0) }.map_err(|_| DecodeError::Backend)?;
let packed = unsafe { mt.GetUINT64(&MF_MT_FRAME_SIZE) }.map_err(|_| DecodeError::Backend)?;
let (width, height) = unpack_u32_pair(packed);
if width == 0 || height == 0 {
return Err(DecodeError::Backend);
}
Ok((width, height))
}
pub(super) fn notify_end_streaming(transform: &IMFTransform) {
unsafe {
let _: windows::core::Result<()> =
transform.ProcessMessage(MFT_MESSAGE_NOTIFY_END_STREAMING, 0);
}
}