use core::ffi::c_void;
use core::ptr;
use apple_cf::cv::CVPixelBuffer;
use crate::error::VTError;
use crate::ffi;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Rotation {
None,
Clockwise90,
Half180,
CounterClockwise90,
}
impl Rotation {
fn as_cf_string(self) -> ffi::CFStringRef {
unsafe {
match self {
Self::None => ffi::kVTRotation_0,
Self::Clockwise90 => ffi::kVTRotation_CW90,
Self::Half180 => ffi::kVTRotation_180,
Self::CounterClockwise90 => ffi::kVTRotation_CCW90,
}
}
}
}
pub struct PixelTransferSession {
session: ffi::VTPixelTransferSessionRef,
}
unsafe impl Send for PixelTransferSession {}
unsafe impl Sync for PixelTransferSession {}
impl Drop for PixelTransferSession {
fn drop(&mut self) {
if !self.session.is_null() {
unsafe {
ffi::VTPixelTransferSessionInvalidate(self.session);
ffi::CFRelease(self.session.cast());
}
self.session = ptr::null_mut();
}
}
}
impl PixelTransferSession {
pub fn new() -> Result<Self, VTError> {
let mut session: ffi::VTPixelTransferSessionRef = ptr::null_mut();
let status =
unsafe { ffi::VTPixelTransferSessionCreate(ffi::kCFAllocatorDefault, &mut session) };
if status != 0 || session.is_null() {
return Err(VTError::SessionCreateFailed(status));
}
Ok(Self { session })
}
pub fn transfer(&self, src: &CVPixelBuffer, dst: &CVPixelBuffer) -> Result<(), VTError> {
let status = unsafe {
ffi::VTPixelTransferSessionTransferImage(
self.session,
src.as_ptr().cast::<c_void>(),
dst.as_ptr().cast::<c_void>(),
)
};
if status == 0 {
Ok(())
} else {
Err(VTError::EncodeFailed(status))
}
}
pub unsafe fn set_property(
&self,
key: ffi::CFStringRef,
value: ffi::CFTypeRef,
) -> Result<(), VTError> {
let status = ffi::VTSessionSetProperty(self.session, key, value);
if status != 0 {
return Err(VTError::SetPropertyFailed {
key: "<custom>".to_string(),
status,
});
}
Ok(())
}
}
pub struct PixelRotationSession {
session: ffi::VTPixelRotationSessionRef,
}
unsafe impl Send for PixelRotationSession {}
unsafe impl Sync for PixelRotationSession {}
impl Drop for PixelRotationSession {
fn drop(&mut self) {
if !self.session.is_null() {
unsafe {
ffi::VTPixelRotationSessionInvalidate(self.session);
ffi::CFRelease(self.session.cast());
}
self.session = ptr::null_mut();
}
}
}
impl PixelRotationSession {
pub fn new() -> Result<Self, VTError> {
let mut session: ffi::VTPixelRotationSessionRef = ptr::null_mut();
let status =
unsafe { ffi::VTPixelRotationSessionCreate(ffi::kCFAllocatorDefault, &mut session) };
if status != 0 || session.is_null() {
return Err(VTError::SessionCreateFailed(status));
}
Ok(Self { session })
}
pub fn set_rotation(&self, rotation: Rotation) -> Result<(), VTError> {
let v = rotation.as_cf_string();
let status = unsafe {
ffi::VTSessionSetProperty(self.session, ffi::kVTPixelRotationPropertyKey_Rotation, v.cast())
};
if status != 0 {
return Err(VTError::SetPropertyFailed {
key: "Rotation".to_string(),
status,
});
}
Ok(())
}
pub fn set_flip_horizontal(&self, flip: bool) -> Result<(), VTError> {
let cf = unsafe {
if flip {
ffi::kCFBooleanTrue
} else {
ffi::kCFBooleanFalse
}
};
let status = unsafe {
ffi::VTSessionSetProperty(
self.session,
ffi::kVTPixelRotationPropertyKey_FlipHorizontalOrientation,
cf,
)
};
if status != 0 {
return Err(VTError::SetPropertyFailed {
key: "FlipHorizontalOrientation".to_string(),
status,
});
}
Ok(())
}
pub fn set_flip_vertical(&self, flip: bool) -> Result<(), VTError> {
let cf = unsafe {
if flip {
ffi::kCFBooleanTrue
} else {
ffi::kCFBooleanFalse
}
};
let status = unsafe {
ffi::VTSessionSetProperty(
self.session,
ffi::kVTPixelRotationPropertyKey_FlipVerticalOrientation,
cf,
)
};
if status != 0 {
return Err(VTError::SetPropertyFailed {
key: "FlipVerticalOrientation".to_string(),
status,
});
}
Ok(())
}
pub fn rotate(&self, src: &CVPixelBuffer, dst: &CVPixelBuffer) -> Result<(), VTError> {
let status = unsafe {
ffi::VTPixelRotationSessionRotateImage(
self.session,
src.as_ptr().cast::<c_void>(),
dst.as_ptr().cast::<c_void>(),
)
};
if status == 0 {
Ok(())
} else {
Err(VTError::EncodeFailed(status))
}
}
}