screencapturekit/cv/pixel_buffer.rs
1//! `CVPixelBuffer` - Video pixel buffer
2
3use super::ffi;
4use crate::cm::IOSurface;
5use std::fmt;
6use std::io::{self, Read, Seek, SeekFrom};
7
8/// Lock flags for `CVPixelBuffer`
9///
10/// This is a bitmask type matching Apple's `CVPixelBufferLockFlags`.
11///
12/// # Examples
13///
14/// ```
15/// use screencapturekit::cv::CVPixelBufferLockFlags;
16///
17/// // Read-only lock
18/// let flags = CVPixelBufferLockFlags::READ_ONLY;
19/// assert!(flags.is_read_only());
20///
21/// // Read-write lock (default)
22/// let flags = CVPixelBufferLockFlags::NONE;
23/// assert!(!flags.is_read_only());
24/// ```
25#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
26pub struct CVPixelBufferLockFlags(u32);
27
28impl CVPixelBufferLockFlags {
29 /// No special options (read-write lock)
30 pub const NONE: Self = Self(0);
31
32 /// Read-only lock - use when you only need to read data.
33 /// This allows Core Video to keep caches valid.
34 pub const READ_ONLY: Self = Self(0x0000_0001);
35
36 /// Create from a raw u32 value
37 #[must_use]
38 pub const fn from_bits(bits: u32) -> Self {
39 Self(bits)
40 }
41
42 /// Convert to u32 for FFI
43 #[must_use]
44 pub const fn as_u32(self) -> u32 {
45 self.0
46 }
47
48 /// Check if this is a read-only lock
49 #[must_use]
50 pub const fn is_read_only(self) -> bool {
51 (self.0 & Self::READ_ONLY.0) != 0
52 }
53
54 /// Check if no flags are set (read-write lock)
55 #[must_use]
56 pub const fn is_empty(self) -> bool {
57 self.0 == 0
58 }
59}
60
61impl From<CVPixelBufferLockFlags> for u32 {
62 fn from(flags: CVPixelBufferLockFlags) -> Self {
63 flags.0
64 }
65}
66
67#[derive(Debug)]
68pub struct CVPixelBuffer(*mut std::ffi::c_void);
69
70impl PartialEq for CVPixelBuffer {
71 fn eq(&self, other: &Self) -> bool {
72 self.0 == other.0
73 }
74}
75
76impl Eq for CVPixelBuffer {}
77
78impl std::hash::Hash for CVPixelBuffer {
79 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
80 unsafe {
81 let hash_value = ffi::cv_pixel_buffer_hash(self.0);
82 hash_value.hash(state);
83 }
84 }
85}
86
87impl CVPixelBuffer {
88 pub fn from_raw(ptr: *mut std::ffi::c_void) -> Option<Self> {
89 if ptr.is_null() {
90 None
91 } else {
92 Some(Self(ptr))
93 }
94 }
95
96 /// # Safety
97 /// The caller must ensure the pointer is a valid `CVPixelBuffer` pointer.
98 pub unsafe fn from_ptr(ptr: *mut std::ffi::c_void) -> Self {
99 Self(ptr)
100 }
101
102 pub fn as_ptr(&self) -> *mut std::ffi::c_void {
103 self.0
104 }
105
106 /// Create a new pixel buffer with the specified dimensions and pixel format
107 ///
108 /// # Arguments
109 ///
110 /// * `width` - Width of the pixel buffer in pixels
111 /// * `height` - Height of the pixel buffer in pixels
112 /// * `pixel_format` - Pixel format type (e.g., 0x42475241 for BGRA)
113 ///
114 /// # Errors
115 ///
116 /// Returns a Core Video error code if the pixel buffer creation fails.
117 ///
118 /// # Examples
119 ///
120 /// ```
121 /// use screencapturekit::cv::CVPixelBuffer;
122 ///
123 /// // Create a 1920x1080 BGRA pixel buffer
124 /// let buffer = CVPixelBuffer::create(1920, 1080, 0x42475241)
125 /// .expect("Failed to create pixel buffer");
126 ///
127 /// assert_eq!(buffer.width(), 1920);
128 /// assert_eq!(buffer.height(), 1080);
129 /// assert_eq!(buffer.pixel_format(), 0x42475241);
130 /// ```
131 pub fn create(width: usize, height: usize, pixel_format: u32) -> Result<Self, i32> {
132 unsafe {
133 let mut pixel_buffer_ptr: *mut std::ffi::c_void = std::ptr::null_mut();
134 let status =
135 ffi::cv_pixel_buffer_create(width, height, pixel_format, &mut pixel_buffer_ptr);
136
137 if status == 0 && !pixel_buffer_ptr.is_null() {
138 Ok(Self(pixel_buffer_ptr))
139 } else {
140 Err(status)
141 }
142 }
143 }
144
145 /// Create a pixel buffer from existing memory
146 ///
147 /// # Arguments
148 ///
149 /// * `width` - Width of the pixel buffer in pixels
150 /// * `height` - Height of the pixel buffer in pixels
151 /// * `pixel_format` - Pixel format type (e.g., 0x42475241 for BGRA)
152 /// * `base_address` - Pointer to pixel data
153 /// * `bytes_per_row` - Number of bytes per row
154 ///
155 /// # Safety
156 ///
157 /// The caller must ensure that:
158 /// - `base_address` points to valid memory
159 /// - Memory remains valid for the lifetime of the pixel buffer
160 /// - `bytes_per_row` correctly represents the memory layout
161 ///
162 /// # Errors
163 ///
164 /// Returns a Core Video error code if the pixel buffer creation fails.
165 ///
166 /// # Examples
167 ///
168 /// ```
169 /// use screencapturekit::cv::CVPixelBuffer;
170 ///
171 /// // Create pixel data (100x100 BGRA image)
172 /// let width = 100;
173 /// let height = 100;
174 /// let bytes_per_pixel = 4; // BGRA
175 /// let bytes_per_row = width * bytes_per_pixel;
176 /// let mut pixel_data = vec![0u8; width * height * bytes_per_pixel];
177 ///
178 /// // Fill with blue color
179 /// for y in 0..height {
180 /// for x in 0..width {
181 /// let offset = y * bytes_per_row + x * bytes_per_pixel;
182 /// pixel_data[offset] = 255; // B
183 /// pixel_data[offset + 1] = 0; // G
184 /// pixel_data[offset + 2] = 0; // R
185 /// pixel_data[offset + 3] = 255; // A
186 /// }
187 /// }
188 ///
189 /// // Create pixel buffer from the data
190 /// let buffer = unsafe {
191 /// CVPixelBuffer::create_with_bytes(
192 /// width,
193 /// height,
194 /// 0x42475241, // BGRA
195 /// pixel_data.as_mut_ptr() as *mut std::ffi::c_void,
196 /// bytes_per_row,
197 /// )
198 /// }.expect("Failed to create pixel buffer");
199 ///
200 /// assert_eq!(buffer.width(), width);
201 /// assert_eq!(buffer.height(), height);
202 /// ```
203 pub unsafe fn create_with_bytes(
204 width: usize,
205 height: usize,
206 pixel_format: u32,
207 base_address: *mut std::ffi::c_void,
208 bytes_per_row: usize,
209 ) -> Result<Self, i32> {
210 let mut pixel_buffer_ptr: *mut std::ffi::c_void = std::ptr::null_mut();
211 let status = ffi::cv_pixel_buffer_create_with_bytes(
212 width,
213 height,
214 pixel_format,
215 base_address,
216 bytes_per_row,
217 &mut pixel_buffer_ptr,
218 );
219
220 if status == 0 && !pixel_buffer_ptr.is_null() {
221 Ok(Self(pixel_buffer_ptr))
222 } else {
223 Err(status)
224 }
225 }
226
227 /// Fill the extended pixels of a pixel buffer
228 ///
229 /// This is useful for pixel buffers that have been created with extended pixels
230 /// enabled, to ensure proper edge handling for effects and filters.
231 ///
232 /// # Errors
233 ///
234 /// Returns a Core Video error code if the operation fails.
235 pub fn fill_extended_pixels(&self) -> Result<(), i32> {
236 unsafe {
237 let status = ffi::cv_pixel_buffer_fill_extended_pixels(self.0);
238 if status == 0 {
239 Ok(())
240 } else {
241 Err(status)
242 }
243 }
244 }
245
246 /// Create a pixel buffer with planar bytes
247 ///
248 /// # Safety
249 ///
250 /// The caller must ensure that:
251 /// - `plane_base_addresses` points to valid memory for each plane
252 /// - Memory remains valid for the lifetime of the pixel buffer
253 /// - All plane parameters correctly represent the memory layout
254 ///
255 /// # Errors
256 ///
257 /// Returns a Core Video error code if the pixel buffer creation fails.
258 pub unsafe fn create_with_planar_bytes(
259 width: usize,
260 height: usize,
261 pixel_format: u32,
262 plane_base_addresses: &[*mut std::ffi::c_void],
263 plane_widths: &[usize],
264 plane_heights: &[usize],
265 plane_bytes_per_row: &[usize],
266 ) -> Result<Self, i32> {
267 if plane_base_addresses.len() != plane_widths.len()
268 || plane_widths.len() != plane_heights.len()
269 || plane_heights.len() != plane_bytes_per_row.len()
270 {
271 return Err(-50); // paramErr
272 }
273
274 let mut pixel_buffer_ptr: *mut std::ffi::c_void = std::ptr::null_mut();
275 let status = ffi::cv_pixel_buffer_create_with_planar_bytes(
276 width,
277 height,
278 pixel_format,
279 plane_base_addresses.len(),
280 plane_base_addresses.as_ptr(),
281 plane_widths.as_ptr(),
282 plane_heights.as_ptr(),
283 plane_bytes_per_row.as_ptr(),
284 &mut pixel_buffer_ptr,
285 );
286
287 if status == 0 && !pixel_buffer_ptr.is_null() {
288 Ok(Self(pixel_buffer_ptr))
289 } else {
290 Err(status)
291 }
292 }
293
294 /// Create a pixel buffer from an `IOSurface`
295 ///
296 /// # Errors
297 ///
298 /// Returns a Core Video error code if the pixel buffer creation fails.
299 pub fn create_with_io_surface(surface: &IOSurface) -> Result<Self, i32> {
300 unsafe {
301 let mut pixel_buffer_ptr: *mut std::ffi::c_void = std::ptr::null_mut();
302 let status = ffi::cv_pixel_buffer_create_with_io_surface(
303 surface.as_ptr(),
304 &mut pixel_buffer_ptr,
305 );
306
307 if status == 0 && !pixel_buffer_ptr.is_null() {
308 Ok(Self(pixel_buffer_ptr))
309 } else {
310 Err(status)
311 }
312 }
313 }
314
315 /// Get the Core Foundation type ID for `CVPixelBuffer`
316 pub fn type_id() -> usize {
317 unsafe { ffi::cv_pixel_buffer_get_type_id() }
318 }
319
320 /// Get the data size of the pixel buffer
321 pub fn data_size(&self) -> usize {
322 unsafe { ffi::cv_pixel_buffer_get_data_size(self.0) }
323 }
324
325 /// Check if the pixel buffer is planar
326 pub fn is_planar(&self) -> bool {
327 unsafe { ffi::cv_pixel_buffer_is_planar(self.0) }
328 }
329
330 /// Get the number of planes in the pixel buffer
331 pub fn plane_count(&self) -> usize {
332 unsafe { ffi::cv_pixel_buffer_get_plane_count(self.0) }
333 }
334
335 /// Get the width of a specific plane
336 pub fn width_of_plane(&self, plane_index: usize) -> usize {
337 unsafe { ffi::cv_pixel_buffer_get_width_of_plane(self.0, plane_index) }
338 }
339
340 /// Get the height of a specific plane
341 pub fn height_of_plane(&self, plane_index: usize) -> usize {
342 unsafe { ffi::cv_pixel_buffer_get_height_of_plane(self.0, plane_index) }
343 }
344
345 /// Get the base address of a specific plane (internal use only)
346 ///
347 /// # Safety
348 /// Caller must ensure the buffer is locked before accessing the returned pointer.
349 fn base_address_of_plane_raw(&self, plane_index: usize) -> Option<*mut u8> {
350 unsafe {
351 let ptr = ffi::cv_pixel_buffer_get_base_address_of_plane(self.0, plane_index);
352 if ptr.is_null() {
353 None
354 } else {
355 Some(ptr.cast::<u8>())
356 }
357 }
358 }
359
360 /// Get the bytes per row of a specific plane
361 pub fn bytes_per_row_of_plane(&self, plane_index: usize) -> usize {
362 unsafe { ffi::cv_pixel_buffer_get_bytes_per_row_of_plane(self.0, plane_index) }
363 }
364
365 /// Get the extended pixel information (left, right, top, bottom)
366 pub fn extended_pixels(&self) -> (usize, usize, usize, usize) {
367 unsafe {
368 let mut left: usize = 0;
369 let mut right: usize = 0;
370 let mut top: usize = 0;
371 let mut bottom: usize = 0;
372 ffi::cv_pixel_buffer_get_extended_pixels(
373 self.0,
374 &mut left,
375 &mut right,
376 &mut top,
377 &mut bottom,
378 );
379 (left, right, top, bottom)
380 }
381 }
382
383 /// Check if the pixel buffer is backed by an `IOSurface`
384 pub fn is_backed_by_io_surface(&self) -> bool {
385 self.io_surface().is_some()
386 }
387
388 /// Get the width of the pixel buffer in pixels
389 pub fn width(&self) -> usize {
390 unsafe { ffi::cv_pixel_buffer_get_width(self.0) }
391 }
392
393 pub fn height(&self) -> usize {
394 unsafe { ffi::cv_pixel_buffer_get_height(self.0) }
395 }
396
397 pub fn pixel_format(&self) -> u32 {
398 unsafe { ffi::cv_pixel_buffer_get_pixel_format_type(self.0) }
399 }
400
401 pub fn bytes_per_row(&self) -> usize {
402 unsafe { ffi::cv_pixel_buffer_get_bytes_per_row(self.0) }
403 }
404
405 /// Lock the base address for raw access
406 ///
407 /// # Errors
408 ///
409 /// Returns a Core Video error code if the lock operation fails.
410 pub fn lock_raw(&self, flags: CVPixelBufferLockFlags) -> Result<(), i32> {
411 unsafe {
412 let result = ffi::cv_pixel_buffer_lock_base_address(self.0, flags.as_u32());
413 if result == 0 {
414 Ok(())
415 } else {
416 Err(result)
417 }
418 }
419 }
420
421 /// Unlock the base address after raw access
422 ///
423 /// # Errors
424 ///
425 /// Returns a Core Video error code if the unlock operation fails.
426 pub fn unlock_raw(&self, flags: CVPixelBufferLockFlags) -> Result<(), i32> {
427 unsafe {
428 let result = ffi::cv_pixel_buffer_unlock_base_address(self.0, flags.as_u32());
429 if result == 0 {
430 Ok(())
431 } else {
432 Err(result)
433 }
434 }
435 }
436
437 /// Get the base address (internal use only)
438 ///
439 /// # Safety
440 /// Caller must ensure the buffer is locked before accessing the returned pointer.
441 fn base_address_raw(&self) -> Option<*mut u8> {
442 unsafe {
443 let ptr = ffi::cv_pixel_buffer_get_base_address(self.0);
444 if ptr.is_null() {
445 None
446 } else {
447 Some(ptr.cast::<u8>())
448 }
449 }
450 }
451
452 /// Get the `IOSurface` backing this pixel buffer
453 pub fn io_surface(&self) -> Option<IOSurface> {
454 unsafe {
455 let ptr = ffi::cv_pixel_buffer_get_io_surface(self.0);
456 IOSurface::from_raw(ptr)
457 }
458 }
459
460 /// Lock the base address and return a guard for RAII-style access
461 ///
462 /// # Arguments
463 ///
464 /// * `flags` - Lock flags (use `CVPixelBufferLockFlags::READ_ONLY` for read-only access)
465 ///
466 /// # Errors
467 ///
468 /// Returns a Core Video error code if the lock operation fails.
469 ///
470 /// # Examples
471 ///
472 /// ```no_run
473 /// use screencapturekit::cv::{CVPixelBuffer, CVPixelBufferLockFlags};
474 ///
475 /// fn read_buffer(buffer: &CVPixelBuffer) {
476 /// let guard = buffer.lock(CVPixelBufferLockFlags::READ_ONLY).unwrap();
477 /// let data = guard.as_slice();
478 /// println!("Buffer has {} bytes", data.len());
479 /// // Buffer automatically unlocked when guard drops
480 /// }
481 /// ```
482 pub fn lock(&self, flags: CVPixelBufferLockFlags) -> Result<CVPixelBufferLockGuard<'_>, i32> {
483 self.lock_raw(flags)?;
484 Ok(CVPixelBufferLockGuard {
485 buffer: self,
486 flags,
487 })
488 }
489
490 /// Lock the base address for read-only access
491 ///
492 /// This is a convenience method equivalent to `lock(CVPixelBufferLockFlags::READ_ONLY)`.
493 ///
494 /// # Errors
495 ///
496 /// Returns a Core Video error code if the lock operation fails.
497 pub fn lock_read_only(&self) -> Result<CVPixelBufferLockGuard<'_>, i32> {
498 self.lock(CVPixelBufferLockFlags::READ_ONLY)
499 }
500
501 /// Lock the base address for read-write access
502 ///
503 /// This is a convenience method equivalent to `lock(CVPixelBufferLockFlags::NONE)`.
504 ///
505 /// # Errors
506 ///
507 /// Returns a Core Video error code if the lock operation fails.
508 pub fn lock_read_write(&self) -> Result<CVPixelBufferLockGuard<'_>, i32> {
509 self.lock(CVPixelBufferLockFlags::NONE)
510 }
511}
512
513/// RAII guard for locked `CVPixelBuffer` base address
514pub struct CVPixelBufferLockGuard<'a> {
515 buffer: &'a CVPixelBuffer,
516 flags: CVPixelBufferLockFlags,
517}
518
519impl CVPixelBufferLockGuard<'_> {
520 /// Get the base address of the locked buffer
521 pub fn base_address(&self) -> *const u8 {
522 self.buffer
523 .base_address_raw()
524 .unwrap_or(std::ptr::null_mut())
525 .cast_const()
526 }
527
528 /// Get mutable base address (only valid for read-write locks)
529 ///
530 /// Returns `None` if this is a read-only lock.
531 pub fn base_address_mut(&mut self) -> Option<*mut u8> {
532 if self.flags.is_read_only() {
533 None
534 } else {
535 self.buffer.base_address_raw()
536 }
537 }
538
539 /// Get the base address of a specific plane
540 ///
541 /// For multi-planar formats like YCbCr 4:2:0:
542 /// - Plane 0: Y (luminance) data
543 /// - Plane 1: `CbCr` (chrominance) data
544 ///
545 /// Returns `None` if the plane index is out of bounds.
546 pub fn base_address_of_plane(&self, plane_index: usize) -> Option<*const u8> {
547 self.buffer
548 .base_address_of_plane_raw(plane_index)
549 .map(<*mut u8>::cast_const)
550 }
551
552 /// Get the mutable base address of a specific plane
553 ///
554 /// Returns `None` if this is a read-only lock or the plane index is out of bounds.
555 pub fn base_address_of_plane_mut(&mut self, plane_index: usize) -> Option<*mut u8> {
556 if self.flags.is_read_only() {
557 return None;
558 }
559 self.buffer.base_address_of_plane_raw(plane_index)
560 }
561
562 /// Get the width of the buffer
563 pub fn width(&self) -> usize {
564 self.buffer.width()
565 }
566
567 /// Get the height of the buffer
568 pub fn height(&self) -> usize {
569 self.buffer.height()
570 }
571
572 /// Get bytes per row
573 pub fn bytes_per_row(&self) -> usize {
574 self.buffer.bytes_per_row()
575 }
576
577 /// Get the data size in bytes
578 ///
579 /// This provides API parity with `IOSurfaceLockGuard::data_size()`.
580 pub fn data_size(&self) -> usize {
581 self.buffer.data_size()
582 }
583
584 /// Get the number of planes
585 pub fn plane_count(&self) -> usize {
586 self.buffer.plane_count()
587 }
588
589 /// Get the width of a specific plane
590 pub fn width_of_plane(&self, plane_index: usize) -> usize {
591 self.buffer.width_of_plane(plane_index)
592 }
593
594 /// Get the height of a specific plane
595 pub fn height_of_plane(&self, plane_index: usize) -> usize {
596 self.buffer.height_of_plane(plane_index)
597 }
598
599 /// Get the bytes per row of a specific plane
600 pub fn bytes_per_row_of_plane(&self, plane_index: usize) -> usize {
601 self.buffer.bytes_per_row_of_plane(plane_index)
602 }
603
604 /// Get data as a byte slice
605 ///
606 /// The lock guard ensures the buffer is locked for the lifetime of the slice.
607 pub fn as_slice(&self) -> &[u8] {
608 let ptr = self.base_address();
609 let len = self.buffer.height() * self.buffer.bytes_per_row();
610 if ptr.is_null() || len == 0 {
611 &[]
612 } else {
613 unsafe { std::slice::from_raw_parts(ptr, len) }
614 }
615 }
616
617 /// Get data as a mutable byte slice (only valid for read-write locks)
618 ///
619 /// Returns `None` if this is a read-only lock.
620 pub fn as_slice_mut(&mut self) -> Option<&mut [u8]> {
621 let ptr = self.base_address_mut()?;
622 let len = self.buffer.height() * self.buffer.bytes_per_row();
623 if ptr.is_null() || len == 0 {
624 Some(&mut [])
625 } else {
626 Some(unsafe { std::slice::from_raw_parts_mut(ptr, len) })
627 }
628 }
629
630 /// Get a slice of plane data
631 ///
632 /// Returns the data for a specific plane as a byte slice.
633 ///
634 /// Returns `None` if the plane index is out of bounds.
635 pub fn plane_data(&self, plane_index: usize) -> Option<&[u8]> {
636 let base = self.base_address_of_plane(plane_index)?;
637 let height = self.buffer.height_of_plane(plane_index);
638 let bytes_per_row = self.buffer.bytes_per_row_of_plane(plane_index);
639 Some(unsafe { std::slice::from_raw_parts(base, height * bytes_per_row) })
640 }
641
642 /// Get a specific row from a plane as a slice
643 ///
644 /// Returns `None` if the plane or row index is out of bounds.
645 pub fn plane_row(&self, plane_index: usize, row_index: usize) -> Option<&[u8]> {
646 if !self.buffer.is_planar() || plane_index >= self.buffer.plane_count() {
647 return None;
648 }
649 let height = self.buffer.height_of_plane(plane_index);
650 if row_index >= height {
651 return None;
652 }
653 let base = self.base_address_of_plane(plane_index)?;
654 let bytes_per_row = self.buffer.bytes_per_row_of_plane(plane_index);
655 Some(unsafe {
656 std::slice::from_raw_parts(base.add(row_index * bytes_per_row), bytes_per_row)
657 })
658 }
659
660 /// Get a specific row as a slice
661 ///
662 /// Returns `None` if the row index is out of bounds.
663 pub fn row(&self, row_index: usize) -> Option<&[u8]> {
664 if row_index >= self.height() {
665 return None;
666 }
667 let ptr = self.base_address();
668 if ptr.is_null() {
669 return None;
670 }
671 unsafe {
672 let row_ptr = ptr.add(row_index * self.bytes_per_row());
673 Some(std::slice::from_raw_parts(row_ptr, self.bytes_per_row()))
674 }
675 }
676
677 /// Access buffer with a standard `std::io::Cursor`
678 ///
679 /// Returns a cursor over the buffer data that implements `Read` and `Seek`.
680 ///
681 /// # Examples
682 ///
683 /// ```no_run
684 /// use screencapturekit::cv::{CVPixelBuffer, CVPixelBufferLockFlags};
685 /// use std::io::{Read, Seek, SeekFrom};
686 ///
687 /// fn read_buffer(buffer: &CVPixelBuffer) {
688 /// let guard = buffer.lock(CVPixelBufferLockFlags::READ_ONLY).unwrap();
689 /// let mut cursor = guard.cursor();
690 ///
691 /// // Read first 4 bytes
692 /// let mut pixel = [0u8; 4];
693 /// cursor.read_exact(&mut pixel).unwrap();
694 ///
695 /// // Seek to row 10
696 /// let offset = 10 * guard.bytes_per_row();
697 /// cursor.seek(SeekFrom::Start(offset as u64)).unwrap();
698 /// }
699 /// ```
700 pub fn cursor(&self) -> io::Cursor<&[u8]> {
701 io::Cursor::new(self.as_slice())
702 }
703
704 /// Get raw pointer to buffer data
705 pub fn as_ptr(&self) -> *const u8 {
706 self.base_address()
707 }
708
709 /// Get mutable raw pointer to buffer data (only valid for read-write locks)
710 ///
711 /// Returns `None` if this is a read-only lock.
712 pub fn as_mut_ptr(&mut self) -> Option<*mut u8> {
713 self.base_address_mut()
714 }
715
716 /// Check if this is a read-only lock
717 pub const fn is_read_only(&self) -> bool {
718 self.flags.is_read_only()
719 }
720
721 /// Get the lock options
722 pub const fn options(&self) -> CVPixelBufferLockFlags {
723 self.flags
724 }
725
726 /// Get the pixel format
727 pub fn pixel_format(&self) -> u32 {
728 self.buffer.pixel_format()
729 }
730}
731
732impl Drop for CVPixelBufferLockGuard<'_> {
733 fn drop(&mut self) {
734 let _ = self.buffer.unlock_raw(self.flags);
735 }
736}
737
738impl std::fmt::Debug for CVPixelBufferLockGuard<'_> {
739 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
740 f.debug_struct("CVPixelBufferLockGuard")
741 .field("flags", &self.flags)
742 .field("buffer_size", &(self.buffer.width(), self.buffer.height()))
743 .finish()
744 }
745}
746
747impl std::ops::Deref for CVPixelBufferLockGuard<'_> {
748 type Target = [u8];
749
750 fn deref(&self) -> &Self::Target {
751 self.as_slice()
752 }
753}
754
755impl Clone for CVPixelBuffer {
756 fn clone(&self) -> Self {
757 unsafe {
758 let ptr = ffi::cv_pixel_buffer_retain(self.0);
759 Self(ptr)
760 }
761 }
762}
763
764impl Drop for CVPixelBuffer {
765 fn drop(&mut self) {
766 unsafe {
767 ffi::cv_pixel_buffer_release(self.0);
768 }
769 }
770}
771
772unsafe impl Send for CVPixelBuffer {}
773unsafe impl Sync for CVPixelBuffer {}
774
775impl fmt::Display for CVPixelBuffer {
776 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
777 write!(
778 f,
779 "CVPixelBuffer({}x{}, format: 0x{:08X})",
780 self.width(),
781 self.height(),
782 self.pixel_format()
783 )
784 }
785}
786
787/// Opaque handle to `CVPixelBufferPool`
788#[repr(transparent)]
789#[derive(Debug)]
790pub struct CVPixelBufferPool(*mut std::ffi::c_void);
791
792impl PartialEq for CVPixelBufferPool {
793 fn eq(&self, other: &Self) -> bool {
794 self.0 == other.0
795 }
796}
797
798impl Eq for CVPixelBufferPool {}
799
800impl std::hash::Hash for CVPixelBufferPool {
801 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
802 unsafe {
803 let hash_value = ffi::cv_pixel_buffer_pool_hash(self.0);
804 hash_value.hash(state);
805 }
806 }
807}
808
809impl CVPixelBufferPool {
810 pub fn from_raw(ptr: *mut std::ffi::c_void) -> Option<Self> {
811 if ptr.is_null() {
812 None
813 } else {
814 Some(Self(ptr))
815 }
816 }
817
818 /// # Safety
819 /// The caller must ensure the pointer is a valid `CVPixelBufferPool` pointer.
820 pub unsafe fn from_ptr(ptr: *mut std::ffi::c_void) -> Self {
821 Self(ptr)
822 }
823
824 pub fn as_ptr(&self) -> *mut std::ffi::c_void {
825 self.0
826 }
827
828 /// Create a new pixel buffer pool
829 ///
830 /// # Arguments
831 ///
832 /// * `width` - Width of pixel buffers in the pool
833 /// * `height` - Height of pixel buffers in the pool
834 /// * `pixel_format` - Pixel format type
835 /// * `max_buffers` - Maximum number of buffers in the pool (0 for unlimited)
836 ///
837 /// # Errors
838 ///
839 /// Returns a Core Video error code if the pool creation fails.
840 pub fn create(
841 width: usize,
842 height: usize,
843 pixel_format: u32,
844 max_buffers: usize,
845 ) -> Result<Self, i32> {
846 unsafe {
847 let mut pool_ptr: *mut std::ffi::c_void = std::ptr::null_mut();
848 let status = ffi::cv_pixel_buffer_pool_create(
849 width,
850 height,
851 pixel_format,
852 max_buffers,
853 &mut pool_ptr,
854 );
855
856 if status == 0 && !pool_ptr.is_null() {
857 Ok(Self(pool_ptr))
858 } else {
859 Err(status)
860 }
861 }
862 }
863
864 /// Create a pixel buffer from the pool
865 ///
866 /// # Errors
867 ///
868 /// Returns a Core Video error code if the buffer creation fails.
869 pub fn create_pixel_buffer(&self) -> Result<CVPixelBuffer, i32> {
870 unsafe {
871 let mut pixel_buffer_ptr: *mut std::ffi::c_void = std::ptr::null_mut();
872 let status =
873 ffi::cv_pixel_buffer_pool_create_pixel_buffer(self.0, &mut pixel_buffer_ptr);
874
875 if status == 0 && !pixel_buffer_ptr.is_null() {
876 Ok(CVPixelBuffer(pixel_buffer_ptr))
877 } else {
878 Err(status)
879 }
880 }
881 }
882
883 /// Flush the pixel buffer pool
884 ///
885 /// Releases all available pixel buffers in the pool
886 pub fn flush(&self) {
887 unsafe {
888 ffi::cv_pixel_buffer_pool_flush(self.0);
889 }
890 }
891
892 /// Get the Core Foundation type ID for `CVPixelBufferPool`
893 pub fn type_id() -> usize {
894 unsafe { ffi::cv_pixel_buffer_pool_get_type_id() }
895 }
896
897 /// Create a pixel buffer from the pool with auxiliary attributes
898 ///
899 /// This allows specifying additional attributes for the created buffer
900 ///
901 /// # Errors
902 ///
903 /// Returns a Core Video error code if the buffer creation fails.
904 pub fn create_pixel_buffer_with_aux_attributes(
905 &self,
906 aux_attributes: Option<&std::collections::HashMap<String, u32>>,
907 ) -> Result<CVPixelBuffer, i32> {
908 // For now, ignore aux_attributes since we don't have a way to pass them through
909 // In a full implementation, this would convert the HashMap to a CFDictionary
910 let _ = aux_attributes;
911 self.create_pixel_buffer()
912 }
913
914 /// Try to create a pixel buffer from the pool without blocking
915 ///
916 /// Returns None if no buffers are available
917 pub fn try_create_pixel_buffer(&self) -> Option<CVPixelBuffer> {
918 self.create_pixel_buffer().ok()
919 }
920
921 /// Flush the pool with specific options
922 ///
923 /// Releases buffers based on the provided flags
924 pub fn flush_with_options(&self, _flags: u32) {
925 // For now, just call regular flush
926 // In a full implementation, this would pass flags to the Swift side
927 self.flush();
928 }
929
930 /// Check if the pool is empty (no available buffers)
931 ///
932 /// Note: This is an approximation based on whether we can create a buffer
933 pub fn is_empty(&self) -> bool {
934 self.try_create_pixel_buffer().is_none()
935 }
936
937 /// Get the pool attributes
938 ///
939 /// Returns the raw pointer to the `CFDictionary` containing pool attributes
940 pub fn attributes(&self) -> Option<*const std::ffi::c_void> {
941 unsafe {
942 let ptr = ffi::cv_pixel_buffer_pool_get_attributes(self.0);
943 if ptr.is_null() {
944 None
945 } else {
946 Some(ptr)
947 }
948 }
949 }
950
951 /// Get the pixel buffer attributes
952 ///
953 /// Returns the raw pointer to the `CFDictionary` containing pixel buffer attributes
954 pub fn pixel_buffer_attributes(&self) -> Option<*const std::ffi::c_void> {
955 unsafe {
956 let ptr = ffi::cv_pixel_buffer_pool_get_pixel_buffer_attributes(self.0);
957 if ptr.is_null() {
958 None
959 } else {
960 Some(ptr)
961 }
962 }
963 }
964}
965
966impl Clone for CVPixelBufferPool {
967 fn clone(&self) -> Self {
968 unsafe {
969 let ptr = ffi::cv_pixel_buffer_pool_retain(self.0);
970 Self(ptr)
971 }
972 }
973}
974
975impl Drop for CVPixelBufferPool {
976 fn drop(&mut self) {
977 unsafe {
978 ffi::cv_pixel_buffer_pool_release(self.0);
979 }
980 }
981}
982
983unsafe impl Send for CVPixelBufferPool {}
984unsafe impl Sync for CVPixelBufferPool {}
985
986impl fmt::Display for CVPixelBufferPool {
987 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
988 write!(f, "CVPixelBufferPool")
989 }
990}
991
992/// Extension trait for `io::Cursor` to add pixel buffer specific operations
993pub trait PixelBufferCursorExt {
994 /// Seek to a specific pixel coordinate (x, y)
995 ///
996 /// Assumes 4 bytes per pixel (BGRA format).
997 ///
998 /// # Errors
999 ///
1000 /// Returns an I/O error if the seek operation fails.
1001 fn seek_to_pixel(&mut self, x: usize, y: usize, bytes_per_row: usize) -> io::Result<u64>;
1002
1003 /// Read a single pixel (4 bytes: BGRA)
1004 ///
1005 /// # Errors
1006 ///
1007 /// Returns an I/O error if the read operation fails.
1008 fn read_pixel(&mut self) -> io::Result<[u8; 4]>;
1009}
1010
1011impl<T: AsRef<[u8]>> PixelBufferCursorExt for io::Cursor<T> {
1012 fn seek_to_pixel(&mut self, x: usize, y: usize, bytes_per_row: usize) -> io::Result<u64> {
1013 let pos = y * bytes_per_row + x * 4; // 4 bytes per pixel (BGRA)
1014 self.seek(SeekFrom::Start(pos as u64))
1015 }
1016
1017 fn read_pixel(&mut self) -> io::Result<[u8; 4]> {
1018 let mut pixel = [0u8; 4];
1019 self.read_exact(&mut pixel)?;
1020 Ok(pixel)
1021 }
1022}