1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
#![cfg(target_os = "macos")]
#![cfg(feature = "iosurface")]

use std::os::raw::c_void;

use std::error::Error;
use std::fmt::Display;

use crate::{platform::{macos::{frame::MacosVideoFrame, objc_wrap::IOSurfaceRef}, platform_impl::objc_wrap::{IOSurfaceDecrementUseCount, IOSurfaceIncrementUseCount}}, prelude::VideoFrame};

/// A MacOS IOSurface instance
pub struct IoSurface(IOSurfaceRef);

impl IoSurface {
    /// Gets the raw IOSurfaceRef
    pub fn get_raw(&self) -> *const c_void {
        self.0
    }

    pub(crate) fn from_ref_unretained(r: IOSurfaceRef) -> Self {
        unsafe { IOSurfaceIncrementUseCount(r); }
        IoSurface(r)
    }
}

impl Clone for IoSurface {
    fn clone(&self) -> Self {
        unsafe { IOSurfaceIncrementUseCount(self.0); }
        IoSurface(self.0)
    }
}

impl Drop for IoSurface {
    fn drop(&mut self) {
        unsafe { IOSurfaceDecrementUseCount(self.0); }
    }
}

/// A video frame which can inter-operate with any MacOS GPU API using IOSurfaces
pub trait MacosIoSurfaceVideoFrameExt {
    /// Get the IOSurface representing the video frame's texture
    fn get_iosurface(&self) -> Result<IoSurface, GetIoSurfaceError>;
}

#[derive(Debug)]
/// Represents an error when getting the IOSurface behind this video frame
pub enum GetIoSurfaceError{
    /// There was no image buffer in this frame
    NoImageBuffer,
    /// There was no IOSurface in the frame's image buffer
    NoIoSurface
}

impl Display for GetIoSurfaceError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Self::NoImageBuffer => f.write_str("GetIoSurfaceError::NoImageBuffer"),
            Self::NoIoSurface => f.write_str("GetIoSurfaceError::NoIoSurface"),
        }
    }
}

impl Error for GetIoSurfaceError {
    fn source(&self) -> Option<&(dyn Error + 'static)> {
        None
    }

    fn description(&self) -> &str {
        "description() is deprecated; use Display"
    }

    fn cause(&self) -> Option<&dyn Error> {
        self.source()
    }
}

impl MacosIoSurfaceVideoFrameExt for VideoFrame {
    fn get_iosurface(&self) -> Result<IoSurface, GetIoSurfaceError> {
        match &self.impl_video_frame {
            MacosVideoFrame::SCStream(frame) => {
                match frame.sample_buffer.get_image_buffer() {
                    Some(image_buffer) => {
                        match image_buffer.get_iosurface_ptr() {
                            Some(ptr) => {
                                Ok(IoSurface::from_ref_unretained(ptr))
                            },
                            None => Err(GetIoSurfaceError::NoIoSurface)
                        }
                    },
                    None => Err(GetIoSurfaceError::NoImageBuffer)
                }
            },
            MacosVideoFrame::CGDisplayStream(frame) => {
                Ok(IoSurface::from_ref_unretained(frame.io_surface.0))
            }
        }
    }
}