use crate::cf::{AsCFType, CFDictionary, CFNumber, CFString};
use crate::iosurface::IOSurface;
use crate::{ffi, raw};
use std::collections::HashMap;
use std::fmt;
use std::io::{self, Read, Seek, SeekFrom};
use std::sync::Arc;
#[repr(transparent)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub struct CVPixelBufferLockFlags(u64);
impl CVPixelBufferLockFlags {
pub const NONE: Self = Self(0);
pub const READ_ONLY: Self = Self(0x0000_0001);
#[must_use]
pub const fn from_bits(bits: u64) -> Self {
Self(bits)
}
#[must_use]
pub const fn bits(self) -> u64 {
self.0
}
#[must_use]
pub const fn is_read_only(self) -> bool {
(self.0 & Self::READ_ONLY.0) != 0
}
#[must_use]
pub const fn is_empty(self) -> bool {
self.0 == 0
}
}
impl From<CVPixelBufferLockFlags> for u64 {
fn from(flags: CVPixelBufferLockFlags) -> Self {
flags.0
}
}
#[derive(Debug)]
pub struct CVPixelBuffer(*mut std::ffi::c_void);
impl PartialEq for CVPixelBuffer {
fn eq(&self, other: &Self) -> bool {
self.0 == other.0
}
}
impl Eq for CVPixelBuffer {}
impl std::hash::Hash for CVPixelBuffer {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
unsafe {
let hash_value = ffi::cv_pixel_buffer_hash(self.0);
hash_value.hash(state);
}
}
}
impl CVPixelBuffer {
pub unsafe fn from_raw(ptr: *mut std::ffi::c_void) -> Option<Self> {
if ptr.is_null() {
None
} else {
Some(Self(ptr))
}
}
#[must_use]
pub unsafe fn from_raw_borrowed(ptr: *mut std::ffi::c_void) -> Option<Self> {
if ptr.is_null() {
None
} else {
let retained = unsafe { ffi::cv_pixel_buffer_retain(ptr) };
unsafe { Self::from_raw(retained) }
}
}
pub const unsafe fn from_ptr(ptr: *mut std::ffi::c_void) -> Self {
Self(ptr)
}
#[must_use]
pub const fn as_ptr(&self) -> *mut std::ffi::c_void {
self.0
}
pub fn create(width: usize, height: usize, pixel_format: u32) -> Result<Self, i32> {
unsafe {
let mut pixel_buffer_ptr: *mut std::ffi::c_void = std::ptr::null_mut();
let status =
ffi::cv_pixel_buffer_create(width, height, pixel_format, &mut pixel_buffer_ptr);
if status == 0 && !pixel_buffer_ptr.is_null() {
Ok(Self(pixel_buffer_ptr))
} else {
Err(status)
}
}
}
pub unsafe fn create_with_bytes(
width: usize,
height: usize,
pixel_format: u32,
base_address: *mut std::ffi::c_void,
bytes_per_row: usize,
) -> Result<Self, i32> {
let mut pixel_buffer_ptr: *mut std::ffi::c_void = std::ptr::null_mut();
let status = ffi::cv_pixel_buffer_create_with_bytes(
width,
height,
pixel_format,
base_address,
bytes_per_row,
&mut pixel_buffer_ptr,
);
if status == 0 && !pixel_buffer_ptr.is_null() {
Ok(Self(pixel_buffer_ptr))
} else {
Err(status)
}
}
pub fn fill_extended_pixels(&self) -> Result<(), i32> {
unsafe {
let status = ffi::cv_pixel_buffer_fill_extended_pixels(self.0);
if status == 0 {
Ok(())
} else {
Err(status)
}
}
}
pub unsafe fn create_with_planar_bytes(
width: usize,
height: usize,
pixel_format: u32,
plane_base_addresses: &[*mut std::ffi::c_void],
plane_widths: &[usize],
plane_heights: &[usize],
plane_bytes_per_row: &[usize],
) -> Result<Self, i32> {
if plane_base_addresses.len() != plane_widths.len()
|| plane_widths.len() != plane_heights.len()
|| plane_heights.len() != plane_bytes_per_row.len()
{
return Err(-50); }
let mut pixel_buffer_ptr: *mut std::ffi::c_void = std::ptr::null_mut();
let status = ffi::cv_pixel_buffer_create_with_planar_bytes(
width,
height,
pixel_format,
plane_base_addresses.len(),
plane_base_addresses.as_ptr(),
plane_widths.as_ptr(),
plane_heights.as_ptr(),
plane_bytes_per_row.as_ptr(),
&mut pixel_buffer_ptr,
);
if status == 0 && !pixel_buffer_ptr.is_null() {
Ok(Self(pixel_buffer_ptr))
} else {
Err(status)
}
}
pub fn create_with_io_surface(surface: &IOSurface) -> Result<Self, i32> {
unsafe {
let mut pixel_buffer_ptr: *mut std::ffi::c_void = std::ptr::null_mut();
let status = ffi::cv_pixel_buffer_create_with_io_surface(
surface.as_ptr(),
&mut pixel_buffer_ptr,
);
if status == 0 && !pixel_buffer_ptr.is_null() {
Ok(Self(pixel_buffer_ptr))
} else {
Err(status)
}
}
}
#[must_use]
pub fn type_id() -> usize {
unsafe { ffi::cv_pixel_buffer_get_type_id() }
}
#[must_use]
pub fn data_size(&self) -> usize {
unsafe { ffi::cv_pixel_buffer_get_data_size(self.0) }
}
#[must_use]
pub fn is_planar(&self) -> bool {
unsafe { ffi::cv_pixel_buffer_is_planar(self.0) }
}
#[must_use]
pub fn plane_count(&self) -> usize {
unsafe { ffi::cv_pixel_buffer_get_plane_count(self.0) }
}
#[must_use]
pub fn width_of_plane(&self, plane_index: usize) -> usize {
unsafe { ffi::cv_pixel_buffer_get_width_of_plane(self.0, plane_index) }
}
#[must_use]
pub fn height_of_plane(&self, plane_index: usize) -> usize {
unsafe { ffi::cv_pixel_buffer_get_height_of_plane(self.0, plane_index) }
}
fn base_address_of_plane_raw(&self, plane_index: usize) -> Option<*mut u8> {
unsafe {
let ptr = ffi::cv_pixel_buffer_get_base_address_of_plane(self.0, plane_index);
if ptr.is_null() {
None
} else {
Some(ptr.cast::<u8>())
}
}
}
#[must_use]
pub fn bytes_per_row_of_plane(&self, plane_index: usize) -> usize {
unsafe { ffi::cv_pixel_buffer_get_bytes_per_row_of_plane(self.0, plane_index) }
}
#[must_use]
pub fn extended_pixels(&self) -> (usize, usize, usize, usize) {
unsafe {
let mut left: usize = 0;
let mut right: usize = 0;
let mut top: usize = 0;
let mut bottom: usize = 0;
ffi::cv_pixel_buffer_get_extended_pixels(
self.0,
&mut left,
&mut right,
&mut top,
&mut bottom,
);
(left, right, top, bottom)
}
}
#[must_use]
pub fn is_backed_by_io_surface(&self) -> bool {
self.io_surface().is_some()
}
#[must_use]
pub fn width(&self) -> usize {
unsafe { ffi::cv_pixel_buffer_get_width(self.0) }
}
#[must_use]
pub fn height(&self) -> usize {
unsafe { ffi::cv_pixel_buffer_get_height(self.0) }
}
#[must_use]
pub fn pixel_format(&self) -> u32 {
unsafe { ffi::cv_pixel_buffer_get_pixel_format_type(self.0) }
}
#[must_use]
pub fn bytes_per_row(&self) -> usize {
unsafe { ffi::cv_pixel_buffer_get_bytes_per_row(self.0) }
}
pub unsafe fn lock_raw(&self, flags: CVPixelBufferLockFlags) -> Result<(), i32> {
let result = unsafe { raw::CVPixelBufferLockBaseAddress(self.0.cast(), flags.bits()) };
if result == 0 {
Ok(())
} else {
Err(result)
}
}
pub unsafe fn unlock_raw(&self, flags: CVPixelBufferLockFlags) -> Result<(), i32> {
let result = unsafe { raw::CVPixelBufferUnlockBaseAddress(self.0.cast(), flags.bits()) };
if result == 0 {
Ok(())
} else {
Err(result)
}
}
fn base_address_raw(&self) -> Option<*mut u8> {
unsafe {
let ptr = ffi::cv_pixel_buffer_get_base_address(self.0);
if ptr.is_null() {
None
} else {
Some(ptr.cast::<u8>())
}
}
}
#[must_use]
pub fn io_surface(&self) -> Option<IOSurface> {
unsafe {
let ptr = ffi::cv_pixel_buffer_get_io_surface(self.0);
IOSurface::from_raw(ptr)
}
}
pub fn lock(&self, flags: CVPixelBufferLockFlags) -> Result<CVPixelBufferLockGuard<'_>, i32> {
unsafe { self.lock_raw(flags)? };
Ok(CVPixelBufferLockGuard {
buffer: self,
flags,
})
}
pub fn lock_read_only(&self) -> Result<CVPixelBufferLockGuard<'_>, i32> {
self.lock(CVPixelBufferLockFlags::READ_ONLY)
}
pub fn lock_read_write(&self) -> Result<CVPixelBufferLockGuard<'_>, i32> {
self.lock(CVPixelBufferLockFlags::NONE)
}
}
pub struct CVPixelBufferLockGuard<'a> {
buffer: &'a CVPixelBuffer,
flags: CVPixelBufferLockFlags,
}
impl CVPixelBufferLockGuard<'_> {
fn non_planar_data_len(&self) -> Option<usize> {
if self.buffer.is_planar() {
return None;
}
let len = self.height().checked_mul(self.bytes_per_row())?;
(len <= self.data_size() && isize::try_from(len).is_ok()).then_some(len)
}
#[must_use]
pub fn base_address(&self) -> *const u8 {
self.buffer
.base_address_raw()
.unwrap_or(std::ptr::null_mut())
.cast_const()
}
pub fn base_address_mut(&mut self) -> Option<*mut u8> {
if self.flags.is_read_only() {
None
} else {
self.buffer.base_address_raw()
}
}
pub fn base_address_of_plane(&self, plane_index: usize) -> Option<*const u8> {
self.buffer
.base_address_of_plane_raw(plane_index)
.map(<*mut u8>::cast_const)
}
pub fn base_address_of_plane_mut(&mut self, plane_index: usize) -> Option<*mut u8> {
if self.flags.is_read_only() {
return None;
}
self.buffer.base_address_of_plane_raw(plane_index)
}
#[must_use]
pub fn width(&self) -> usize {
self.buffer.width()
}
#[must_use]
pub fn height(&self) -> usize {
self.buffer.height()
}
#[must_use]
pub fn bytes_per_row(&self) -> usize {
self.buffer.bytes_per_row()
}
#[must_use]
pub fn data_size(&self) -> usize {
self.buffer.data_size()
}
#[must_use]
pub fn plane_count(&self) -> usize {
self.buffer.plane_count()
}
#[must_use]
pub fn width_of_plane(&self, plane_index: usize) -> usize {
self.buffer.width_of_plane(plane_index)
}
#[must_use]
pub fn height_of_plane(&self, plane_index: usize) -> usize {
self.buffer.height_of_plane(plane_index)
}
#[must_use]
pub fn bytes_per_row_of_plane(&self, plane_index: usize) -> usize {
self.buffer.bytes_per_row_of_plane(plane_index)
}
#[must_use]
pub unsafe fn as_slice(&self) -> Option<&[u8]> {
let ptr = self.base_address();
let len = self.non_planar_data_len()?;
if len == 0 {
return Some(&[]);
}
if ptr.is_null() {
return None;
}
Some(unsafe { std::slice::from_raw_parts(ptr, len) })
}
pub unsafe fn as_slice_mut(&mut self) -> Option<&mut [u8]> {
let len = self.non_planar_data_len()?;
if len == 0 {
return Some(&mut []);
}
let ptr = self.base_address_mut()?;
Some(unsafe { std::slice::from_raw_parts_mut(ptr, len) })
}
#[must_use]
pub unsafe fn plane_data(&self, plane_index: usize) -> Option<&[u8]> {
if !self.buffer.is_planar() || plane_index >= self.buffer.plane_count() {
return None;
}
let base = self.base_address_of_plane(plane_index)?;
let height = self.buffer.height_of_plane(plane_index);
let bytes_per_row = self.buffer.bytes_per_row_of_plane(plane_index);
let len = height.checked_mul(bytes_per_row)?;
if isize::try_from(len).is_err() {
return None;
}
Some(unsafe { std::slice::from_raw_parts(base, len) })
}
#[must_use]
pub unsafe fn plane_row(&self, plane_index: usize, row_index: usize) -> Option<&[u8]> {
if !self.buffer.is_planar() || plane_index >= self.buffer.plane_count() {
return None;
}
let height = self.buffer.height_of_plane(plane_index);
if row_index >= height {
return None;
}
let base = self.base_address_of_plane(plane_index)?;
let bytes_per_row = self.buffer.bytes_per_row_of_plane(plane_index);
let plane_len = height.checked_mul(bytes_per_row)?;
let offset = row_index.checked_mul(bytes_per_row)?;
let end = offset.checked_add(bytes_per_row)?;
if end > plane_len || isize::try_from(bytes_per_row).is_err() {
return None;
}
Some(unsafe { std::slice::from_raw_parts(base.add(offset), bytes_per_row) })
}
#[must_use]
pub unsafe fn row(&self, row_index: usize) -> Option<&[u8]> {
if row_index >= self.height() {
return None;
}
let len = self.non_planar_data_len()?;
let ptr = self.base_address();
if ptr.is_null() {
return None;
}
let bytes_per_row = self.bytes_per_row();
let offset = row_index.checked_mul(bytes_per_row)?;
let end = offset.checked_add(bytes_per_row)?;
if end > len || isize::try_from(bytes_per_row).is_err() {
return None;
}
Some(unsafe { std::slice::from_raw_parts(ptr.add(offset), bytes_per_row) })
}
#[must_use]
pub unsafe fn cursor(&self) -> Option<io::Cursor<&[u8]>> {
unsafe { self.as_slice() }.map(io::Cursor::new)
}
#[must_use]
pub fn as_ptr(&self) -> *const u8 {
self.base_address()
}
pub fn as_mut_ptr(&mut self) -> Option<*mut u8> {
self.base_address_mut()
}
#[must_use]
pub const fn is_read_only(&self) -> bool {
self.flags.is_read_only()
}
#[must_use]
pub const fn options(&self) -> CVPixelBufferLockFlags {
self.flags
}
#[must_use]
pub fn pixel_format(&self) -> u32 {
self.buffer.pixel_format()
}
}
impl Drop for CVPixelBufferLockGuard<'_> {
fn drop(&mut self) {
let _ = unsafe { self.buffer.unlock_raw(self.flags) };
}
}
impl std::fmt::Debug for CVPixelBufferLockGuard<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("CVPixelBufferLockGuard")
.field("flags", &self.flags)
.field("buffer_size", &(self.buffer.width(), self.buffer.height()))
.finish()
}
}
crate::utils::retained::cf_retained!(
CVPixelBuffer,
retain = ffi::cv_pixel_buffer_retain,
release = ffi::cv_pixel_buffer_release,
);
unsafe impl Send for CVPixelBuffer {}
unsafe impl Sync for CVPixelBuffer {}
impl fmt::Display for CVPixelBuffer {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"CVPixelBuffer({}x{}, format: 0x{:08X})",
self.width(),
self.height(),
self.pixel_format()
)
}
}
#[repr(transparent)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub struct CVPixelBufferPoolFlushFlags(u64);
impl CVPixelBufferPoolFlushFlags {
pub const NONE: Self = Self(0);
pub const EXCESS_BUFFERS: Self = Self(1);
#[must_use]
pub const fn from_bits(bits: u64) -> Self {
Self(bits)
}
#[must_use]
pub const fn bits(self) -> u64 {
self.0
}
}
impl std::ops::BitOr for CVPixelBufferPoolFlushFlags {
type Output = Self;
fn bitor(self, rhs: Self) -> Self::Output {
Self(self.0 | rhs.0)
}
}
impl std::ops::BitOrAssign for CVPixelBufferPoolFlushFlags {
fn bitor_assign(&mut self, rhs: Self) {
self.0 |= rhs.0;
}
}
impl From<CVPixelBufferPoolFlushFlags> for u64 {
fn from(flags: CVPixelBufferPoolFlushFlags) -> Self {
flags.bits()
}
}
const CV_RETURN_WOULD_EXCEED_ALLOCATION_THRESHOLD: i32 = -6689;
const PARAM_ERR: i32 = -50;
#[derive(Debug)]
struct CVPixelBufferPoolPolicy {
max_buffers: Option<usize>,
allocation_attributes: Option<CFDictionary>,
}
unsafe impl Send for CVPixelBufferPoolPolicy {}
unsafe impl Sync for CVPixelBufferPoolPolicy {}
impl CVPixelBufferPoolPolicy {
const fn unlimited() -> Self {
Self {
max_buffers: None,
allocation_attributes: None,
}
}
fn new(max_buffers: usize) -> Result<Self, i32> {
if max_buffers == 0 {
return Ok(Self::unlimited());
}
let threshold = i64::try_from(max_buffers).map_err(|_| PARAM_ERR)?;
let key = retained_cf_string(
unsafe { raw::kCVPixelBufferPoolAllocationThresholdKey },
"kCVPixelBufferPoolAllocationThresholdKey",
);
let value = CFNumber::from_i64(threshold);
let attributes = CFDictionary::from_pairs(&[(&key, &value)]);
Ok(Self {
max_buffers: Some(max_buffers),
allocation_attributes: Some(attributes),
})
}
const fn max_buffers(&self) -> usize {
match self.max_buffers {
Some(max_buffers) => max_buffers,
None => 0,
}
}
}
fn retained_cf_string(ptr: raw::CFStringRef, symbol: &'static str) -> CFString {
unsafe { CFString::from_raw_borrowed(ptr.cast_mut().cast()) }
.unwrap_or_else(|| panic!("{symbol} was NULL"))
}
fn pool_pixel_buffer_attributes(
width: usize,
height: usize,
pixel_format: u32,
) -> Result<CFDictionary, i32> {
let width = u64::try_from(width).map_err(|_| PARAM_ERR)?;
let height = u64::try_from(height).map_err(|_| PARAM_ERR)?;
let width_key = retained_cf_string(
unsafe { raw::kCVPixelBufferWidthKey },
"kCVPixelBufferWidthKey",
);
let height_key = retained_cf_string(
unsafe { raw::kCVPixelBufferHeightKey },
"kCVPixelBufferHeightKey",
);
let pixel_format_key = retained_cf_string(
unsafe { raw::kCVPixelBufferPixelFormatTypeKey },
"kCVPixelBufferPixelFormatTypeKey",
);
let io_surface_key = retained_cf_string(
unsafe { raw::kCVPixelBufferIOSurfacePropertiesKey },
"kCVPixelBufferIOSurfacePropertiesKey",
);
let width_value = CFNumber::from_u64(width);
let height_value = CFNumber::from_u64(height);
let pixel_format_value = CFNumber::from_u64(u64::from(pixel_format));
let io_surface_properties = CFDictionary::from_pairs(&[]);
let pairs: [(&dyn AsCFType, &dyn AsCFType); 4] = [
(&width_key, &width_value),
(&height_key, &height_value),
(&pixel_format_key, &pixel_format_value),
(&io_surface_key, &io_surface_properties),
];
Ok(CFDictionary::from_pairs(&pairs))
}
pub struct CVPixelBufferPool {
ptr: *mut std::ffi::c_void,
policy: Arc<CVPixelBufferPoolPolicy>,
}
unsafe impl Send for CVPixelBufferPool {}
unsafe impl Sync for CVPixelBufferPool {}
impl PartialEq for CVPixelBufferPool {
fn eq(&self, other: &Self) -> bool {
self.ptr == other.ptr
}
}
impl Eq for CVPixelBufferPool {}
impl std::hash::Hash for CVPixelBufferPool {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
unsafe { ffi::cf_type_hash(self.ptr) }.hash(state);
}
}
impl CVPixelBufferPool {
pub unsafe fn from_raw(ptr: *mut std::ffi::c_void) -> Option<Self> {
if ptr.is_null() {
None
} else {
Some(Self {
ptr,
policy: Arc::new(CVPixelBufferPoolPolicy::unlimited()),
})
}
}
pub unsafe fn from_raw_with_max_buffers(
ptr: *mut std::ffi::c_void,
max_buffers: usize,
) -> Result<Option<Self>, i32> {
if ptr.is_null() {
return Ok(None);
}
let policy = Arc::new(CVPixelBufferPoolPolicy::new(max_buffers)?);
Ok(Some(Self { ptr, policy }))
}
#[must_use]
pub unsafe fn from_raw_borrowed(ptr: *mut std::ffi::c_void) -> Option<Self> {
if ptr.is_null() {
None
} else {
let retained = unsafe { raw::CVPixelBufferPoolRetain(ptr.cast()) };
unsafe { Self::from_raw(retained.cast()) }
}
}
pub unsafe fn from_raw_borrowed_with_max_buffers(
ptr: *mut std::ffi::c_void,
max_buffers: usize,
) -> Result<Option<Self>, i32> {
if ptr.is_null() {
return Ok(None);
}
let policy = Arc::new(CVPixelBufferPoolPolicy::new(max_buffers)?);
let retained = unsafe { raw::CVPixelBufferPoolRetain(ptr.cast()) };
Ok(Some(Self {
ptr: retained.cast(),
policy,
}))
}
pub unsafe fn from_ptr(ptr: *mut std::ffi::c_void) -> Self {
Self {
ptr,
policy: Arc::new(CVPixelBufferPoolPolicy::unlimited()),
}
}
#[must_use]
pub const fn as_ptr(&self) -> *mut std::ffi::c_void {
self.ptr
}
#[must_use]
pub fn max_buffers(&self) -> usize {
self.policy.max_buffers()
}
pub fn create(
width: usize,
height: usize,
pixel_format: u32,
max_buffers: usize,
) -> Result<Self, i32> {
let policy = Arc::new(CVPixelBufferPoolPolicy::new(max_buffers)?);
let pool_attributes = CFDictionary::from_pairs(&[]);
let pixel_buffer_attributes = pool_pixel_buffer_attributes(width, height, pixel_format)?;
let mut pool_ptr: raw::CVPixelBufferPoolRef = std::ptr::null_mut();
unsafe {
let status = raw::CVPixelBufferPoolCreate(
std::ptr::null(),
pool_attributes.as_ptr().cast(),
pixel_buffer_attributes.as_ptr().cast(),
&mut pool_ptr,
);
if status == 0 && !pool_ptr.is_null() {
Ok(Self {
ptr: pool_ptr.cast(),
policy,
})
} else {
Err(status)
}
}
}
fn create_pixel_buffer_with_dictionary(
&self,
auxiliary_attributes: Option<&CFDictionary>,
) -> Result<CVPixelBuffer, i32> {
let mut pixel_buffer_ptr: raw::CVPixelBufferRef = std::ptr::null_mut();
let status = unsafe {
if let Some(attributes) = auxiliary_attributes {
raw::CVPixelBufferPoolCreatePixelBufferWithAuxAttributes(
std::ptr::null(),
self.ptr.cast(),
attributes.as_ptr().cast(),
&mut pixel_buffer_ptr,
)
} else {
raw::CVPixelBufferPoolCreatePixelBuffer(
std::ptr::null(),
self.ptr.cast(),
&mut pixel_buffer_ptr,
)
}
};
if status == 0 && !pixel_buffer_ptr.is_null() {
unsafe { CVPixelBuffer::from_raw(pixel_buffer_ptr.cast()) }.ok_or(status)
} else {
Err(status)
}
}
pub fn create_pixel_buffer(&self) -> Result<CVPixelBuffer, i32> {
self.create_pixel_buffer_with_dictionary(self.policy.allocation_attributes.as_ref())
}
pub fn flush(&self) {
self.flush_with_flags(CVPixelBufferPoolFlushFlags::NONE);
}
pub fn flush_with_flags(&self, flags: CVPixelBufferPoolFlushFlags) {
unsafe { raw::CVPixelBufferPoolFlush(self.ptr.cast(), flags.bits()) };
}
pub fn flush_excess_buffers(&self) {
self.flush_with_flags(CVPixelBufferPoolFlushFlags::EXCESS_BUFFERS);
}
#[must_use]
pub fn type_id() -> usize {
#[allow(clippy::cast_possible_truncation)]
{
unsafe { raw::CVPixelBufferPoolGetTypeID() as usize }
}
}
pub fn create_pixel_buffer_with_aux_attributes(
&self,
aux_attributes: Option<&HashMap<String, u32>>,
) -> Result<CVPixelBuffer, i32> {
let Some(aux_attributes) = aux_attributes.filter(|attributes| !attributes.is_empty())
else {
return self.create_pixel_buffer();
};
let threshold_key = retained_cf_string(
unsafe { raw::kCVPixelBufferPoolAllocationThresholdKey },
"kCVPixelBufferPoolAllocationThresholdKey",
);
let mut keys = Vec::with_capacity(aux_attributes.len() + 1);
let mut values = Vec::with_capacity(aux_attributes.len() + 1);
let mut requested_threshold = None;
for (key, value) in aux_attributes {
if key.as_bytes().contains(&0) {
return Err(PARAM_ERR);
}
let key = CFString::new(key);
if key == threshold_key {
requested_threshold = Some(usize::try_from(*value).map_err(|_| PARAM_ERR)?);
} else {
keys.push(key);
values.push(CFNumber::from_u64(u64::from(*value)));
}
}
let effective_threshold = match (self.policy.max_buffers, requested_threshold) {
(Some(configured), Some(requested)) => Some(configured.min(requested)),
(Some(configured), None) => Some(configured),
(None, requested) => requested,
};
if let Some(threshold) = effective_threshold {
let threshold = i64::try_from(threshold).map_err(|_| PARAM_ERR)?;
keys.push(threshold_key);
values.push(CFNumber::from_i64(threshold));
}
let pairs: Vec<(&dyn AsCFType, &dyn AsCFType)> = keys
.iter()
.zip(&values)
.map(|(key, value)| (key as &dyn AsCFType, value as &dyn AsCFType))
.collect();
let attributes = CFDictionary::from_pairs(&pairs);
self.create_pixel_buffer_with_dictionary(Some(&attributes))
}
pub fn try_create_pixel_buffer(&self) -> Result<Option<CVPixelBuffer>, i32> {
match self.create_pixel_buffer() {
Ok(buffer) => Ok(Some(buffer)),
Err(CV_RETURN_WOULD_EXCEED_ALLOCATION_THRESHOLD) => Ok(None),
Err(status) => Err(status),
}
}
#[must_use]
pub fn attributes(&self) -> Option<CFDictionary> {
let ptr = unsafe { raw::CVPixelBufferPoolGetAttributes(self.ptr.cast()) };
unsafe { CFDictionary::from_raw_borrowed(ptr.cast_mut().cast()) }
}
#[must_use]
pub fn pixel_buffer_attributes(&self) -> Option<CFDictionary> {
let ptr = unsafe { raw::CVPixelBufferPoolGetPixelBufferAttributes(self.ptr.cast()) };
unsafe { CFDictionary::from_raw_borrowed(ptr.cast_mut().cast()) }
}
}
impl Clone for CVPixelBufferPool {
fn clone(&self) -> Self {
let ptr = unsafe { raw::CVPixelBufferPoolRetain(self.ptr.cast()) };
Self {
ptr: ptr.cast(),
policy: Arc::clone(&self.policy),
}
}
}
impl Drop for CVPixelBufferPool {
fn drop(&mut self) {
if !self.ptr.is_null() {
unsafe { raw::CVPixelBufferPoolRelease(self.ptr.cast()) };
}
}
}
impl fmt::Debug for CVPixelBufferPool {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("CVPixelBufferPool")
.field("ptr", &self.ptr)
.field("max_buffers", &self.max_buffers())
.finish_non_exhaustive()
}
}
impl fmt::Display for CVPixelBufferPool {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "CVPixelBufferPool")
}
}
pub trait PixelBufferCursorExt {
fn seek_to_pixel(&mut self, x: usize, y: usize, bytes_per_row: usize) -> io::Result<u64>;
fn read_pixel(&mut self) -> io::Result<[u8; 4]>;
}
impl<T: AsRef<[u8]>> PixelBufferCursorExt for io::Cursor<T> {
fn seek_to_pixel(&mut self, x: usize, y: usize, bytes_per_row: usize) -> io::Result<u64> {
let pos = y * bytes_per_row + x * 4; self.seek(SeekFrom::Start(pos as u64))
}
fn read_pixel(&mut self) -> io::Result<[u8; 4]> {
let mut pixel = [0u8; 4];
self.read_exact(&mut pixel)?;
Ok(pixel)
}
}