Skip to main content

screencapturekit/cm/
time.rs

1//! Core Media time types
2
3use std::ffi::c_void;
4use std::fmt;
5
6pub use apple_cf::cm::CMTime;
7
8/// Sample timing information
9///
10/// Contains timing data for a media sample (audio or video frame).
11///
12/// # Examples
13///
14/// ```
15/// use screencapturekit::cm::{CMSampleTimingInfo, CMTime};
16///
17/// let timing = CMSampleTimingInfo::new();
18/// assert!(!timing.is_valid());
19///
20/// let duration = CMTime::new(1, 30);
21/// let pts = CMTime::new(100, 30);
22/// let dts = CMTime::new(100, 30);
23/// let timing = CMSampleTimingInfo::with_times(duration, pts, dts);
24/// assert!(timing.is_valid());
25/// ```
26#[repr(C)]
27#[derive(Debug, Clone, Copy, PartialEq, Eq)]
28pub struct CMSampleTimingInfo {
29    pub duration: CMTime,
30    pub presentation_time_stamp: CMTime,
31    pub decode_time_stamp: CMTime,
32}
33
34impl std::hash::Hash for CMSampleTimingInfo {
35    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
36        self.duration.hash(state);
37        self.presentation_time_stamp.hash(state);
38        self.decode_time_stamp.hash(state);
39    }
40}
41
42impl CMSampleTimingInfo {
43    /// Create a new timing info with all times set to invalid
44    ///
45    /// # Examples
46    ///
47    /// ```
48    /// use screencapturekit::cm::CMSampleTimingInfo;
49    ///
50    /// let timing = CMSampleTimingInfo::new();
51    /// assert!(!timing.is_valid());
52    /// ```
53    pub const fn new() -> Self {
54        Self {
55            duration: CMTime::INVALID,
56            presentation_time_stamp: CMTime::INVALID,
57            decode_time_stamp: CMTime::INVALID,
58        }
59    }
60
61    /// Create timing info with specific values
62    pub const fn with_times(
63        duration: CMTime,
64        presentation_time_stamp: CMTime,
65        decode_time_stamp: CMTime,
66    ) -> Self {
67        Self {
68            duration,
69            presentation_time_stamp,
70            decode_time_stamp,
71        }
72    }
73
74    /// Check if all timing fields are valid
75    pub const fn is_valid(&self) -> bool {
76        self.duration.is_valid()
77            && self.presentation_time_stamp.is_valid()
78            && self.decode_time_stamp.is_valid()
79    }
80
81    /// Check if presentation timestamp is valid
82    pub const fn has_valid_presentation_time(&self) -> bool {
83        self.presentation_time_stamp.is_valid()
84    }
85
86    /// Check if decode timestamp is valid
87    pub const fn has_valid_decode_time(&self) -> bool {
88        self.decode_time_stamp.is_valid()
89    }
90
91    /// Check if duration is valid
92    pub const fn has_valid_duration(&self) -> bool {
93        self.duration.is_valid()
94    }
95
96    /// Get the presentation timestamp in seconds
97    pub fn presentation_seconds(&self) -> Option<f64> {
98        self.presentation_time_stamp.as_seconds()
99    }
100
101    /// Get the decode timestamp in seconds
102    pub fn decode_seconds(&self) -> Option<f64> {
103        self.decode_time_stamp.as_seconds()
104    }
105
106    /// Get the duration in seconds
107    pub fn duration_seconds(&self) -> Option<f64> {
108        self.duration.as_seconds()
109    }
110}
111
112impl Default for CMSampleTimingInfo {
113    fn default() -> Self {
114        Self::new()
115    }
116}
117
118impl fmt::Display for CMSampleTimingInfo {
119    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
120        write!(
121            f,
122            "CMSampleTimingInfo(pts: {}, dts: {}, duration: {})",
123            self.presentation_time_stamp, self.decode_time_stamp, self.duration
124        )
125    }
126}
127
128/// `CMClock` wrapper for synchronization clock
129///
130/// Represents a Core Media clock used for time synchronization.
131/// Available on macOS 13.0+.
132pub struct CMClock {
133    ptr: *const c_void,
134}
135
136impl PartialEq for CMClock {
137    fn eq(&self, other: &Self) -> bool {
138        self.ptr == other.ptr
139    }
140}
141
142impl Eq for CMClock {}
143
144impl std::hash::Hash for CMClock {
145    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
146        self.ptr.hash(state);
147    }
148}
149
150impl CMClock {
151    /// Create from raw pointer, returning None if null
152    pub fn from_raw(ptr: *const c_void) -> Option<Self> {
153        if ptr.is_null() {
154            None
155        } else {
156            Some(Self { ptr })
157        }
158    }
159
160    /// Create from raw pointer (used internally)
161    ///
162    /// # Safety
163    /// The caller must ensure the pointer is a valid, retained `CMClock` pointer.
164    #[allow(dead_code)]
165    pub(crate) fn from_ptr(ptr: *const c_void) -> Self {
166        Self { ptr }
167    }
168
169    /// Returns the raw pointer to the underlying `CMClock`
170    pub fn as_ptr(&self) -> *const c_void {
171        self.ptr
172    }
173
174    /// Get the current time from this clock
175    ///
176    /// Note: Returns invalid time. Use `as_ptr()` with Core Media APIs directly
177    /// for full clock functionality.
178    pub fn time(&self) -> CMTime {
179        // This would require FFI to CMClockGetTime - for now return invalid
180        // Users can use the pointer directly with Core Media APIs
181        CMTime::INVALID
182    }
183}
184
185impl Drop for CMClock {
186    fn drop(&mut self) {
187        if !self.ptr.is_null() {
188            // CMClock is a CFType, needs CFRelease
189            extern "C" {
190                fn CFRelease(cf: *const c_void);
191            }
192            unsafe {
193                CFRelease(self.ptr);
194            }
195        }
196    }
197}
198
199impl Clone for CMClock {
200    fn clone(&self) -> Self {
201        if self.ptr.is_null() {
202            Self {
203                ptr: std::ptr::null(),
204            }
205        } else {
206            extern "C" {
207                fn CFRetain(cf: *const c_void) -> *const c_void;
208            }
209            unsafe {
210                Self {
211                    ptr: CFRetain(self.ptr),
212                }
213            }
214        }
215    }
216}
217
218impl std::fmt::Debug for CMClock {
219    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
220        f.debug_struct("CMClock").field("ptr", &self.ptr).finish()
221    }
222}
223
224impl fmt::Display for CMClock {
225    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
226        if self.ptr.is_null() {
227            write!(f, "CMClock(null)")
228        } else {
229            write!(f, "CMClock({:p})", self.ptr)
230        }
231    }
232}
233
234// Safety: CMClock is a CoreFoundation type that is thread-safe
235unsafe impl Send for CMClock {}
236unsafe impl Sync for CMClock {}