use core::ffi::c_void;
use core::ptr;
use apple_cf::cm::CMSampleBuffer;
use crate::error::VTError;
use crate::ffi;
pub struct FrameSilo {
inner: ffi::VTFrameSiloRef,
}
unsafe impl Send for FrameSilo {}
crate::utils::retained::vt_retained!(FrameSilo, field = inner, release = ffi::CFRelease);
impl FrameSilo {
#[must_use]
pub fn type_id() -> usize {
unsafe { ffi::VTFrameSiloGetTypeID() }
}
pub fn new() -> Result<Self, VTError> {
let mut p: ffi::VTFrameSiloRef = ptr::null_mut();
let s = unsafe {
ffi::VTFrameSiloCreate(
ffi::kCFAllocatorDefault,
ptr::null(),
ffi::CMTimeRange::INVALID,
ptr::null(),
&raw mut p,
)
};
if s != 0 || p.is_null() {
return Err(VTError::SessionCreateFailed(s));
}
Ok(Self { inner: p })
}
pub fn add_sample_buffer(&self, sample: &CMSampleBuffer) -> Result<(), VTError> {
let s = unsafe { ffi::VTFrameSiloAddSampleBuffer(self.inner, sample.as_ptr().cast()) };
if s == 0 {
Ok(())
} else {
Err(VTError::EncodeFailed(s))
}
}
pub fn progress_of_current_pass(&self) -> Result<f32, VTError> {
let mut p: f32 = 0.0;
let s = unsafe { ffi::VTFrameSiloGetProgressOfCurrentPass(self.inner, &raw mut p) };
if s == 0 {
Ok(p)
} else {
Err(VTError::EncodeFailed(s))
}
}
pub fn set_time_ranges_for_next_pass(
&self,
time_ranges: &[ffi::CMTimeRange],
) -> Result<(), VTError> {
let count = ffi::CMItemCount::try_from(time_ranges.len()).map_err(|_| {
VTError::InvalidArgument("time range count overflowed CMItemCount".to_string())
})?;
let status = unsafe {
ffi::VTFrameSiloSetTimeRangesForNextPass(self.inner, count, time_ranges.as_ptr())
};
if status == 0 {
Ok(())
} else {
Err(VTError::ApiFailed {
api: "VTFrameSiloSetTimeRangesForNextPass",
status,
})
}
}
pub fn sample_buffers(
&self,
time_range: Option<ffi::CMTimeRange>,
) -> Result<Vec<CMSampleBuffer>, VTError> {
let mut samples = Vec::new();
let status = unsafe {
ffi::VTFrameSiloCallFunctionForEachSampleBuffer(
self.inner,
time_range.unwrap_or(ffi::CMTimeRange::INVALID),
(&raw mut samples).cast(),
Some(frame_silo_collect_sample_buffer),
)
};
if status == 0 {
Ok(samples)
} else {
Err(VTError::ApiFailed {
api: "VTFrameSiloCallFunctionForEachSampleBuffer",
status,
})
}
}
#[must_use]
pub const fn as_ptr(&self) -> ffi::VTFrameSiloRef {
self.inner
}
}
pub struct MultiPassStorage {
inner: ffi::VTMultiPassStorageRef,
}
unsafe impl Send for MultiPassStorage {}
crate::utils::retained::vt_retained!(
MultiPassStorage,
field = inner,
invalidate = ffi::VTMultiPassStorageClose,
release = ffi::CFRelease,
);
impl MultiPassStorage {
#[must_use]
pub fn type_id() -> usize {
unsafe { ffi::VTMultiPassStorageGetTypeID() }
}
pub fn new() -> Result<Self, VTError> {
let mut p: ffi::VTMultiPassStorageRef = ptr::null_mut();
let s = unsafe {
ffi::VTMultiPassStorageCreate(
ffi::kCFAllocatorDefault,
ptr::null(),
ffi::CMTimeRange::INVALID,
ptr::null(),
&raw mut p,
)
};
if s != 0 || p.is_null() {
return Err(VTError::SessionCreateFailed(s));
}
Ok(Self { inner: p })
}
pub fn close(&self) -> Result<(), VTError> {
let status = unsafe { ffi::VTMultiPassStorageClose(self.inner) };
if status == 0 {
Ok(())
} else {
Err(VTError::ApiFailed {
api: "VTMultiPassStorageClose",
status,
})
}
}
#[must_use]
pub const fn as_ptr(&self) -> ffi::VTMultiPassStorageRef {
self.inner
}
}
unsafe extern "C" fn frame_silo_collect_sample_buffer(
refcon: *mut c_void,
sample_buffer: ffi::CMSampleBufferRef,
) -> ffi::OSStatus {
let Some(samples) = (unsafe { refcon.cast::<Vec<CMSampleBuffer>>().as_mut() }) else {
return -1;
};
let Some(sample) = apple_cf::cm::CMSampleBuffer::from_raw_borrowed(sample_buffer.cast()) else {
return -1;
};
samples.push(sample);
0
}