use anyhow::{bail, Result};
use std::ffi::c_void;
use std::ptr;
use crate::frame::VideoFrame;
use super::{
AMF_MEMORY_HOST, AMF_OK, AMF_PLANE_UV, AMF_PLANE_Y, AMF_SURFACE_NV12, AMF_SURFACE_P010,
AmfContextObj, AmfPlaneObj, AmfSurfaceObj, AmfSurfaceVtbl,
};
pub(super) struct SurfaceGuard {
pub(super) surface: *mut c_void,
owned: bool,
}
impl SurfaceGuard {
pub(super) fn new(surface: *mut c_void) -> Self {
Self {
surface,
owned: true,
}
}
pub(super) fn transfer_to_encoder(&mut self) {
self.owned = false;
}
pub(super) fn as_ptr(&self) -> *mut c_void {
self.surface
}
}
impl Drop for SurfaceGuard {
fn drop(&mut self) {
if self.owned && !self.surface.is_null() {
unsafe {
let obj = self.surface as *mut AmfSurfaceObj;
let vt = &*(*obj).vtbl;
(vt.release)(self.surface);
}
}
}
}
#[derive(Clone, Copy)]
pub(super) struct SessionSnapshot {
pub(super) encoder: *mut c_void,
pub(super) context: *mut c_void,
pub(super) width: u32,
pub(super) height: u32,
pub(super) pts_timescale: u64,
pub(super) surface_format: i32,
}
pub(super) unsafe fn upload_frame_static(
snap: &SessionSnapshot,
frame: &VideoFrame,
) -> Result<*mut c_void> {
let _ = snap.encoder; unsafe {
let context_obj = snap.context as *mut AmfContextObj;
let context_vt = &*(*context_obj).vtbl;
let mut surface: *mut c_void = ptr::null_mut();
let rc = (context_vt.alloc_surface)(
snap.context,
AMF_MEMORY_HOST,
snap.surface_format,
snap.width as i32,
snap.height as i32,
&mut surface,
);
if rc != AMF_OK || surface.is_null() {
bail!(
"AMFContext::AllocSurface({}x{} fmt={}) failed: {rc}",
snap.width,
snap.height,
snap.surface_format,
);
}
let surface_obj = surface as *mut AmfSurfaceObj;
let surface_vt = &*(*surface_obj).vtbl;
let y_plane = (surface_vt.get_plane)(surface, AMF_PLANE_Y);
let uv_plane = (surface_vt.get_plane)(surface, AMF_PLANE_UV);
if y_plane.is_null() || uv_plane.is_null() {
(surface_vt.release)(surface);
bail!(
"AMF surface (fmt={}) missing Y or UV plane",
snap.surface_format
);
}
let upload_result = match snap.surface_format {
AMF_SURFACE_NV12 => copy_yuv420p_to_nv12_surface(
surface,
surface_vt,
y_plane,
uv_plane,
snap.width,
snap.height,
frame,
),
AMF_SURFACE_P010 => copy_yuv420p10le_to_p010_surface(
surface,
surface_vt,
y_plane,
uv_plane,
snap.width,
snap.height,
frame,
),
other => {
(surface_vt.release)(surface);
bail!("AMF surface format {other} not supported by uploader");
}
};
upload_result?;
(surface_vt.set_pts)(surface, (frame.pts * snap.pts_timescale) as i64);
Ok(surface)
}
}
unsafe fn copy_yuv420p_to_nv12_surface(
surface: *mut c_void,
surface_vt: &AmfSurfaceVtbl,
y_plane: *mut c_void,
uv_plane: *mut c_void,
width: u32,
height: u32,
frame: &VideoFrame,
) -> Result<()> {
unsafe {
let w = width as usize;
let h = height as usize;
let y_size = w * h;
let cw = w.div_ceil(2);
let ch = h.div_ceil(2);
let uv_size = cw * ch;
if frame.data.len() < y_size + 2 * uv_size {
(surface_vt.release)(surface);
bail!(
"frame data too small for {}x{} YUV420p: need {} bytes, got {}",
w,
h,
y_size + 2 * uv_size,
frame.data.len()
);
}
let y_plane_obj = y_plane as *mut AmfPlaneObj;
let y_vt = &*(*y_plane_obj).vtbl;
let y_dst = (y_vt.get_native)(y_plane) as *mut u8;
let y_pitch = (y_vt.get_h_pitch)(y_plane) as usize;
if y_dst.is_null() {
(surface_vt.release)(surface);
bail!("AMF Y plane native pointer is null — surface not host-mapped?");
}
for row in 0..h {
let src = frame.data.as_ptr().add(row * w);
let dst = y_dst.add(row * y_pitch);
ptr::copy_nonoverlapping(src, dst, w);
}
let uv_plane_obj = uv_plane as *mut AmfPlaneObj;
let uv_vt = &*(*uv_plane_obj).vtbl;
let uv_dst = (uv_vt.get_native)(uv_plane) as *mut u8;
let uv_pitch = (uv_vt.get_h_pitch)(uv_plane) as usize;
if uv_dst.is_null() {
(surface_vt.release)(surface);
bail!("AMF UV plane native pointer is null — surface not host-mapped?");
}
let u_src_base = frame.data.as_ptr().add(y_size);
let v_src_base = u_src_base.add(uv_size);
for row in 0..ch {
let u_src = u_src_base.add(row * cw);
let v_src = v_src_base.add(row * cw);
let dst_row = uv_dst.add(row * uv_pitch);
for col in 0..cw {
*dst_row.add(col * 2) = *u_src.add(col);
*dst_row.add(col * 2 + 1) = *v_src.add(col);
}
}
Ok(())
}
}
unsafe fn copy_yuv420p10le_to_p010_surface(
surface: *mut c_void,
surface_vt: &AmfSurfaceVtbl,
y_plane: *mut c_void,
uv_plane: *mut c_void,
width: u32,
height: u32,
frame: &VideoFrame,
) -> Result<()> {
unsafe {
let w = width as usize;
let h = height as usize;
let cw = w.div_ceil(2);
let ch = h.div_ceil(2);
let y_bytes = w * h * 2;
let uv_bytes = cw * ch * 2;
if frame.data.len() < y_bytes + 2 * uv_bytes {
(surface_vt.release)(surface);
bail!(
"frame data too small for {}x{} Yuv420p10le: need {} bytes, got {}",
w,
h,
y_bytes + 2 * uv_bytes,
frame.data.len()
);
}
let y_plane_obj = y_plane as *mut AmfPlaneObj;
let y_vt = &*(*y_plane_obj).vtbl;
let y_dst = (y_vt.get_native)(y_plane) as *mut u8;
let y_pitch_bytes = (y_vt.get_h_pitch)(y_plane) as usize;
if y_dst.is_null() {
(surface_vt.release)(surface);
bail!("AMF P010 Y plane native pointer is null");
}
let src_ptr = frame.data.as_ptr();
for row in 0..h {
let src_row = src_ptr.add(row * w * 2) as *const u16;
let dst_row = y_dst.add(row * y_pitch_bytes) as *mut u16;
for col in 0..w {
let sample = (*src_row.add(col)) & 0x03FF;
*dst_row.add(col) = sample << 6;
}
}
let uv_plane_obj = uv_plane as *mut AmfPlaneObj;
let uv_vt = &*(*uv_plane_obj).vtbl;
let uv_dst = (uv_vt.get_native)(uv_plane) as *mut u8;
let uv_pitch_bytes = (uv_vt.get_h_pitch)(uv_plane) as usize;
if uv_dst.is_null() {
(surface_vt.release)(surface);
bail!("AMF P010 UV plane native pointer is null");
}
let u_src_base = src_ptr.add(y_bytes);
let v_src_base = u_src_base.add(uv_bytes);
for row in 0..ch {
let u_src = u_src_base.add(row * cw * 2) as *const u16;
let v_src = v_src_base.add(row * cw * 2) as *const u16;
let dst_row = uv_dst.add(row * uv_pitch_bytes) as *mut u16;
for col in 0..cw {
let u = (*u_src.add(col)) & 0x03FF;
let v = (*v_src.add(col)) & 0x03FF;
*dst_row.add(col * 2) = u << 6;
*dst_row.add(col * 2 + 1) = v << 6;
}
}
Ok(())
}
}