screencapturekit/cm/
pixel_buffer.rs

1//! `CVPixelBuffer` - Video pixel buffer
2
3use super::ffi;
4use super::IOSurface;
5use std::fmt;
6
7pub struct CVPixelBuffer(*mut std::ffi::c_void);
8
9impl PartialEq for CVPixelBuffer {
10    fn eq(&self, other: &Self) -> bool {
11        self.0 == other.0
12    }
13}
14
15impl Eq for CVPixelBuffer {}
16
17impl std::hash::Hash for CVPixelBuffer {
18    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
19        unsafe {
20            let hash_value = ffi::cv_pixel_buffer_hash(self.0);
21            hash_value.hash(state);
22        }
23    }
24}
25
26impl CVPixelBuffer {
27    pub fn from_raw(ptr: *mut std::ffi::c_void) -> Option<Self> {
28        if ptr.is_null() {
29            None
30        } else {
31            Some(Self(ptr))
32        }
33    }
34
35    /// # Safety
36    /// The caller must ensure the pointer is a valid `CVPixelBuffer` pointer.
37    pub unsafe fn from_ptr(ptr: *mut std::ffi::c_void) -> Self {
38        Self(ptr)
39    }
40
41    pub fn as_ptr(&self) -> *mut std::ffi::c_void {
42        self.0
43    }
44
45    /// Create a new pixel buffer with the specified dimensions and pixel format
46    ///
47    /// # Arguments
48    ///
49    /// * `width` - Width of the pixel buffer in pixels
50    /// * `height` - Height of the pixel buffer in pixels
51    /// * `pixel_format` - Pixel format type (e.g., 0x42475241 for BGRA)
52    ///
53    /// # Errors
54    ///
55    /// Returns a Core Video error code if the pixel buffer creation fails.
56    ///
57    /// # Examples
58    ///
59    /// ```
60    /// use screencapturekit::cm::CVPixelBuffer;
61    ///
62    /// // Create a 1920x1080 BGRA pixel buffer
63    /// let buffer = CVPixelBuffer::create(1920, 1080, 0x42475241)
64    ///     .expect("Failed to create pixel buffer");
65    ///
66    /// assert_eq!(buffer.width(), 1920);
67    /// assert_eq!(buffer.height(), 1080);
68    /// assert_eq!(buffer.pixel_format(), 0x42475241);
69    /// ```
70    pub fn create(width: usize, height: usize, pixel_format: u32) -> Result<Self, i32> {
71        unsafe {
72            let mut pixel_buffer_ptr: *mut std::ffi::c_void = std::ptr::null_mut();
73            let status =
74                ffi::cv_pixel_buffer_create(width, height, pixel_format, &mut pixel_buffer_ptr);
75
76            if status == 0 && !pixel_buffer_ptr.is_null() {
77                Ok(Self(pixel_buffer_ptr))
78            } else {
79                Err(status)
80            }
81        }
82    }
83
84    /// Create a pixel buffer from existing memory
85    ///
86    /// # Arguments
87    ///
88    /// * `width` - Width of the pixel buffer in pixels
89    /// * `height` - Height of the pixel buffer in pixels
90    /// * `pixel_format` - Pixel format type (e.g., 0x42475241 for BGRA)
91    /// * `base_address` - Pointer to pixel data
92    /// * `bytes_per_row` - Number of bytes per row
93    ///
94    /// # Safety
95    ///
96    /// The caller must ensure that:
97    /// - `base_address` points to valid memory
98    /// - Memory remains valid for the lifetime of the pixel buffer
99    /// - `bytes_per_row` correctly represents the memory layout
100    ///
101    /// # Errors
102    ///
103    /// Returns a Core Video error code if the pixel buffer creation fails.
104    ///
105    /// # Examples
106    ///
107    /// ```
108    /// use screencapturekit::cm::CVPixelBuffer;
109    ///
110    /// // Create pixel data (100x100 BGRA image)
111    /// let width = 100;
112    /// let height = 100;
113    /// let bytes_per_pixel = 4; // BGRA
114    /// let bytes_per_row = width * bytes_per_pixel;
115    /// let mut pixel_data = vec![0u8; width * height * bytes_per_pixel];
116    ///
117    /// // Fill with blue color
118    /// for y in 0..height {
119    ///     for x in 0..width {
120    ///         let offset = y * bytes_per_row + x * bytes_per_pixel;
121    ///         pixel_data[offset] = 255;     // B
122    ///         pixel_data[offset + 1] = 0;   // G
123    ///         pixel_data[offset + 2] = 0;   // R
124    ///         pixel_data[offset + 3] = 255; // A
125    ///     }
126    /// }
127    ///
128    /// // Create pixel buffer from the data
129    /// let buffer = unsafe {
130    ///     CVPixelBuffer::create_with_bytes(
131    ///         width,
132    ///         height,
133    ///         0x42475241, // BGRA
134    ///         pixel_data.as_mut_ptr() as *mut std::ffi::c_void,
135    ///         bytes_per_row,
136    ///     )
137    /// }.expect("Failed to create pixel buffer");
138    ///
139    /// assert_eq!(buffer.width(), width);
140    /// assert_eq!(buffer.height(), height);
141    /// ```
142    pub unsafe fn create_with_bytes(
143        width: usize,
144        height: usize,
145        pixel_format: u32,
146        base_address: *mut std::ffi::c_void,
147        bytes_per_row: usize,
148    ) -> Result<Self, i32> {
149        let mut pixel_buffer_ptr: *mut std::ffi::c_void = std::ptr::null_mut();
150        let status = ffi::cv_pixel_buffer_create_with_bytes(
151            width,
152            height,
153            pixel_format,
154            base_address,
155            bytes_per_row,
156            &mut pixel_buffer_ptr,
157        );
158
159        if status == 0 && !pixel_buffer_ptr.is_null() {
160            Ok(Self(pixel_buffer_ptr))
161        } else {
162            Err(status)
163        }
164    }
165
166    /// Fill the extended pixels of a pixel buffer
167    ///
168    /// This is useful for pixel buffers that have been created with extended pixels
169    /// enabled, to ensure proper edge handling for effects and filters.
170    ///
171    /// # Errors
172    ///
173    /// Returns a Core Video error code if the operation fails.
174    pub fn fill_extended_pixels(&self) -> Result<(), i32> {
175        unsafe {
176            let status = ffi::cv_pixel_buffer_fill_extended_pixels(self.0);
177            if status == 0 {
178                Ok(())
179            } else {
180                Err(status)
181            }
182        }
183    }
184
185    /// Create a pixel buffer with planar bytes
186    ///
187    /// # Safety
188    ///
189    /// The caller must ensure that:
190    /// - `plane_base_addresses` points to valid memory for each plane
191    /// - Memory remains valid for the lifetime of the pixel buffer
192    /// - All plane parameters correctly represent the memory layout
193    ///
194    /// # Errors
195    ///
196    /// Returns a Core Video error code if the pixel buffer creation fails.
197    pub unsafe fn create_with_planar_bytes(
198        width: usize,
199        height: usize,
200        pixel_format: u32,
201        plane_base_addresses: &[*mut std::ffi::c_void],
202        plane_widths: &[usize],
203        plane_heights: &[usize],
204        plane_bytes_per_row: &[usize],
205    ) -> Result<Self, i32> {
206        if plane_base_addresses.len() != plane_widths.len()
207            || plane_widths.len() != plane_heights.len()
208            || plane_heights.len() != plane_bytes_per_row.len()
209        {
210            return Err(-50); // paramErr
211        }
212
213        let mut pixel_buffer_ptr: *mut std::ffi::c_void = std::ptr::null_mut();
214        let status = ffi::cv_pixel_buffer_create_with_planar_bytes(
215            width,
216            height,
217            pixel_format,
218            plane_base_addresses.len(),
219            plane_base_addresses.as_ptr(),
220            plane_widths.as_ptr(),
221            plane_heights.as_ptr(),
222            plane_bytes_per_row.as_ptr(),
223            &mut pixel_buffer_ptr,
224        );
225
226        if status == 0 && !pixel_buffer_ptr.is_null() {
227            Ok(Self(pixel_buffer_ptr))
228        } else {
229            Err(status)
230        }
231    }
232
233    /// Create a pixel buffer from an `IOSurface`
234    ///
235    /// # Errors
236    ///
237    /// Returns a Core Video error code if the pixel buffer creation fails.
238    pub fn create_with_io_surface(surface: &IOSurface) -> Result<Self, i32> {
239        unsafe {
240            let mut pixel_buffer_ptr: *mut std::ffi::c_void = std::ptr::null_mut();
241            let status = ffi::cv_pixel_buffer_create_with_io_surface(
242                surface.as_ptr(),
243                &mut pixel_buffer_ptr,
244            );
245
246            if status == 0 && !pixel_buffer_ptr.is_null() {
247                Ok(Self(pixel_buffer_ptr))
248            } else {
249                Err(status)
250            }
251        }
252    }
253
254    /// Get the Core Foundation type ID for `CVPixelBuffer`
255    pub fn get_type_id() -> usize {
256        unsafe { ffi::cv_pixel_buffer_get_type_id() }
257    }
258
259    /// Get the data size of the pixel buffer
260    pub fn get_data_size(&self) -> usize {
261        unsafe { ffi::cv_pixel_buffer_get_data_size(self.0) }
262    }
263
264    /// Check if the pixel buffer is planar
265    pub fn is_planar(&self) -> bool {
266        unsafe { ffi::cv_pixel_buffer_is_planar(self.0) }
267    }
268
269    /// Get the number of planes in the pixel buffer
270    pub fn get_plane_count(&self) -> usize {
271        unsafe { ffi::cv_pixel_buffer_get_plane_count(self.0) }
272    }
273
274    /// Get the width of a specific plane
275    pub fn get_width_of_plane(&self, plane_index: usize) -> usize {
276        unsafe { ffi::cv_pixel_buffer_get_width_of_plane(self.0, plane_index) }
277    }
278
279    /// Get the height of a specific plane
280    pub fn get_height_of_plane(&self, plane_index: usize) -> usize {
281        unsafe { ffi::cv_pixel_buffer_get_height_of_plane(self.0, plane_index) }
282    }
283
284    /// Get the base address of a specific plane
285    pub fn get_base_address_of_plane(&self, plane_index: usize) -> Option<*mut u8> {
286        unsafe {
287            let ptr = ffi::cv_pixel_buffer_get_base_address_of_plane(self.0, plane_index);
288            if ptr.is_null() {
289                None
290            } else {
291                Some(ptr.cast::<u8>())
292            }
293        }
294    }
295
296    /// Get the bytes per row of a specific plane
297    pub fn get_bytes_per_row_of_plane(&self, plane_index: usize) -> usize {
298        unsafe { ffi::cv_pixel_buffer_get_bytes_per_row_of_plane(self.0, plane_index) }
299    }
300
301    /// Get the extended pixel information (left, right, top, bottom)
302    pub fn get_extended_pixels(&self) -> (usize, usize, usize, usize) {
303        unsafe {
304            let mut left: usize = 0;
305            let mut right: usize = 0;
306            let mut top: usize = 0;
307            let mut bottom: usize = 0;
308            ffi::cv_pixel_buffer_get_extended_pixels(
309                self.0,
310                &mut left,
311                &mut right,
312                &mut top,
313                &mut bottom,
314            );
315            (left, right, top, bottom)
316        }
317    }
318
319    /// Check if the pixel buffer is backed by an `IOSurface`
320    pub fn is_backed_by_io_surface(&self) -> bool {
321        self.get_io_surface().is_some()
322    }
323
324    pub fn get_width(&self) -> usize {
325        self.width()
326    }
327
328    pub fn get_height(&self) -> usize {
329        self.height()
330    }
331
332    pub fn get_pixel_format_type(&self) -> u32 {
333        self.pixel_format()
334    }
335
336    pub fn get_bytes_per_row(&self) -> usize {
337        self.bytes_per_row()
338    }
339
340    pub fn get_base_address(&self) -> Option<*mut u8> {
341        self.base_address()
342    }
343
344    pub fn get_iosurface(&self) -> Option<IOSurface> {
345        self.get_io_surface()
346    }
347
348    pub fn width(&self) -> usize {
349        unsafe { ffi::cv_pixel_buffer_get_width(self.0) }
350    }
351
352    pub fn height(&self) -> usize {
353        unsafe { ffi::cv_pixel_buffer_get_height(self.0) }
354    }
355
356    pub fn pixel_format(&self) -> u32 {
357        unsafe { ffi::cv_pixel_buffer_get_pixel_format_type(self.0) }
358    }
359
360    pub fn bytes_per_row(&self) -> usize {
361        unsafe { ffi::cv_pixel_buffer_get_bytes_per_row(self.0) }
362    }
363
364    /// Lock the base address for raw access
365    ///
366    /// # Errors
367    ///
368    /// Returns a Core Video error code if the lock operation fails.
369    pub fn lock_raw(&self, flags: u32) -> Result<(), i32> {
370        unsafe {
371            let result = ffi::cv_pixel_buffer_lock_base_address(self.0, flags);
372            if result == 0 {
373                Ok(())
374            } else {
375                Err(result)
376            }
377        }
378    }
379
380    /// Unlock the base address after raw access
381    ///
382    /// # Errors
383    ///
384    /// Returns a Core Video error code if the unlock operation fails.
385    pub fn unlock_raw(&self, flags: u32) -> Result<(), i32> {
386        unsafe {
387            let result = ffi::cv_pixel_buffer_unlock_base_address(self.0, flags);
388            if result == 0 {
389                Ok(())
390            } else {
391                Err(result)
392            }
393        }
394    }
395
396    pub fn base_address(&self) -> Option<*mut u8> {
397        unsafe {
398            let ptr = ffi::cv_pixel_buffer_get_base_address(self.0);
399            if ptr.is_null() {
400                None
401            } else {
402                Some(ptr.cast::<u8>())
403            }
404        }
405    }
406
407    pub fn get_io_surface(&self) -> Option<IOSurface> {
408        unsafe {
409            let ptr = ffi::cv_pixel_buffer_get_io_surface(self.0);
410            IOSurface::from_raw(ptr)
411        }
412    }
413
414    /// Lock the base address and return a guard for RAII-style access
415    ///
416    /// # Errors
417    ///
418    /// Returns a Core Video error code if the lock operation fails.
419    pub fn lock_base_address(&self, read_only: bool) -> Result<CVPixelBufferLockGuard<'_>, i32> {
420        let flags = u32::from(read_only);
421        self.lock_raw(flags)?;
422        Ok(CVPixelBufferLockGuard {
423            buffer: self,
424            read_only,
425        })
426    }
427}
428
429/// RAII guard for locked `CVPixelBuffer` base address
430pub struct CVPixelBufferLockGuard<'a> {
431    buffer: &'a CVPixelBuffer,
432    read_only: bool,
433}
434
435impl CVPixelBufferLockGuard<'_> {
436    pub fn get_base_address(&self) -> *const u8 {
437        self.buffer
438            .base_address()
439            .unwrap_or(std::ptr::null_mut())
440            .cast_const()
441    }
442
443    pub fn get_base_address_mut(&mut self) -> *mut u8 {
444        if self.read_only {
445            std::ptr::null_mut()
446        } else {
447            self.buffer.base_address().unwrap_or(std::ptr::null_mut())
448        }
449    }
450}
451
452impl Drop for CVPixelBufferLockGuard<'_> {
453    fn drop(&mut self) {
454        let flags = u32::from(self.read_only);
455        let _ = self.buffer.unlock_raw(flags);
456    }
457}
458
459impl Clone for CVPixelBuffer {
460    fn clone(&self) -> Self {
461        unsafe {
462            let ptr = ffi::cv_pixel_buffer_retain(self.0);
463            Self(ptr)
464        }
465    }
466}
467
468impl Drop for CVPixelBuffer {
469    fn drop(&mut self) {
470        unsafe {
471            ffi::cv_pixel_buffer_release(self.0);
472        }
473    }
474}
475
476unsafe impl Send for CVPixelBuffer {}
477unsafe impl Sync for CVPixelBuffer {}
478
479impl fmt::Display for CVPixelBuffer {
480    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
481        write!(
482            f,
483            "CVPixelBuffer({}x{}, format: 0x{:08X})",
484            self.width(),
485            self.height(),
486            self.pixel_format()
487        )
488    }
489}
490
491/// Opaque handle to `CVPixelBufferPool`
492#[repr(transparent)]
493#[derive(Debug)]
494pub struct CVPixelBufferPool(*mut std::ffi::c_void);
495
496impl PartialEq for CVPixelBufferPool {
497    fn eq(&self, other: &Self) -> bool {
498        self.0 == other.0
499    }
500}
501
502impl Eq for CVPixelBufferPool {}
503
504impl std::hash::Hash for CVPixelBufferPool {
505    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
506        unsafe {
507            let hash_value = ffi::cv_pixel_buffer_pool_hash(self.0);
508            hash_value.hash(state);
509        }
510    }
511}
512
513impl CVPixelBufferPool {
514    pub fn from_raw(ptr: *mut std::ffi::c_void) -> Option<Self> {
515        if ptr.is_null() {
516            None
517        } else {
518            Some(Self(ptr))
519        }
520    }
521
522    /// # Safety
523    /// The caller must ensure the pointer is a valid `CVPixelBufferPool` pointer.
524    pub unsafe fn from_ptr(ptr: *mut std::ffi::c_void) -> Self {
525        Self(ptr)
526    }
527
528    pub fn as_ptr(&self) -> *mut std::ffi::c_void {
529        self.0
530    }
531
532    /// Create a new pixel buffer pool
533    ///
534    /// # Arguments
535    ///
536    /// * `width` - Width of pixel buffers in the pool
537    /// * `height` - Height of pixel buffers in the pool
538    /// * `pixel_format` - Pixel format type
539    /// * `max_buffers` - Maximum number of buffers in the pool (0 for unlimited)
540    ///
541    /// # Errors
542    ///
543    /// Returns a Core Video error code if the pool creation fails.
544    pub fn create(
545        width: usize,
546        height: usize,
547        pixel_format: u32,
548        max_buffers: usize,
549    ) -> Result<Self, i32> {
550        unsafe {
551            let mut pool_ptr: *mut std::ffi::c_void = std::ptr::null_mut();
552            let status = ffi::cv_pixel_buffer_pool_create(
553                width,
554                height,
555                pixel_format,
556                max_buffers,
557                &mut pool_ptr,
558            );
559
560            if status == 0 && !pool_ptr.is_null() {
561                Ok(Self(pool_ptr))
562            } else {
563                Err(status)
564            }
565        }
566    }
567
568    /// Create a pixel buffer from the pool
569    ///
570    /// # Errors
571    ///
572    /// Returns a Core Video error code if the buffer creation fails.
573    pub fn create_pixel_buffer(&self) -> Result<CVPixelBuffer, i32> {
574        unsafe {
575            let mut pixel_buffer_ptr: *mut std::ffi::c_void = std::ptr::null_mut();
576            let status =
577                ffi::cv_pixel_buffer_pool_create_pixel_buffer(self.0, &mut pixel_buffer_ptr);
578
579            if status == 0 && !pixel_buffer_ptr.is_null() {
580                Ok(CVPixelBuffer(pixel_buffer_ptr))
581            } else {
582                Err(status)
583            }
584        }
585    }
586
587    /// Flush the pixel buffer pool
588    ///
589    /// Releases all available pixel buffers in the pool
590    pub fn flush(&self) {
591        unsafe {
592            ffi::cv_pixel_buffer_pool_flush(self.0);
593        }
594    }
595
596    /// Get the Core Foundation type ID for `CVPixelBufferPool`
597    pub fn get_type_id() -> usize {
598        unsafe { ffi::cv_pixel_buffer_pool_get_type_id() }
599    }
600
601    /// Create a pixel buffer from the pool with auxiliary attributes
602    ///
603    /// This allows specifying additional attributes for the created buffer
604    ///
605    /// # Errors
606    ///
607    /// Returns a Core Video error code if the buffer creation fails.
608    pub fn create_pixel_buffer_with_aux_attributes(
609        &self,
610        aux_attributes: Option<&std::collections::HashMap<String, u32>>,
611    ) -> Result<CVPixelBuffer, i32> {
612        // For now, ignore aux_attributes since we don't have a way to pass them through
613        // In a full implementation, this would convert the HashMap to a CFDictionary
614        let _ = aux_attributes;
615        self.create_pixel_buffer()
616    }
617
618    /// Try to create a pixel buffer from the pool without blocking
619    ///
620    /// Returns None if no buffers are available
621    pub fn try_create_pixel_buffer(&self) -> Option<CVPixelBuffer> {
622        self.create_pixel_buffer().ok()
623    }
624
625    /// Flush the pool with specific options
626    ///
627    /// Releases buffers based on the provided flags
628    pub fn flush_with_options(&self, _flags: u32) {
629        // For now, just call regular flush
630        // In a full implementation, this would pass flags to the Swift side
631        self.flush();
632    }
633
634    /// Check if the pool is empty (no available buffers)
635    ///
636    /// Note: This is an approximation based on whether we can create a buffer
637    pub fn is_empty(&self) -> bool {
638        self.try_create_pixel_buffer().is_none()
639    }
640
641    /// Get the pool attributes
642    ///
643    /// Returns the raw pointer to the `CFDictionary` containing pool attributes
644    pub fn get_attributes(&self) -> Option<*const std::ffi::c_void> {
645        unsafe {
646            let ptr = ffi::cv_pixel_buffer_pool_get_attributes(self.0);
647            if ptr.is_null() {
648                None
649            } else {
650                Some(ptr)
651            }
652        }
653    }
654
655    /// Get the pixel buffer attributes
656    ///
657    /// Returns the raw pointer to the `CFDictionary` containing pixel buffer attributes
658    pub fn get_pixel_buffer_attributes(&self) -> Option<*const std::ffi::c_void> {
659        unsafe {
660            let ptr = ffi::cv_pixel_buffer_pool_get_pixel_buffer_attributes(self.0);
661            if ptr.is_null() {
662                None
663            } else {
664                Some(ptr)
665            }
666        }
667    }
668}
669
670impl Clone for CVPixelBufferPool {
671    fn clone(&self) -> Self {
672        unsafe {
673            let ptr = ffi::cv_pixel_buffer_pool_retain(self.0);
674            Self(ptr)
675        }
676    }
677}
678
679impl Drop for CVPixelBufferPool {
680    fn drop(&mut self) {
681        unsafe {
682            ffi::cv_pixel_buffer_pool_release(self.0);
683        }
684    }
685}
686
687unsafe impl Send for CVPixelBufferPool {}
688unsafe impl Sync for CVPixelBufferPool {}
689
690impl fmt::Display for CVPixelBufferPool {
691    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
692        write!(f, "CVPixelBufferPool")
693    }
694}