Skip to main content

apple_cf/cm/
timebase.rs

1//! `CMTimebase` wrapper.
2//!
3#![allow(clippy::missing_errors_doc)]
4
5//! ```rust,no_run
6//! use apple_cf::cm::{CMClock, CMTime, CMTimebase};
7//!
8//! let clock = CMClock::host_time_clock();
9//! let timebase = CMTimebase::with_source_clock(&clock).expect("timebase");
10//! assert!(timebase.time().is_valid());
11//! assert_eq!(timebase.set_rate(1.0), 0);
12//! assert_eq!(timebase.set_time(CMTime::new(0, 600)), 0);
13//! ```
14
15use super::{CMClock, CMTime};
16use std::ffi::c_void;
17use std::fmt;
18
19/// Owned wrapper around `CMTimebaseRef`.
20pub struct CMTimebase {
21    ptr: *const c_void,
22}
23
24impl CMTimebase {
25    /// Create a timebase that uses `source_clock` as its time source.
26    pub fn with_source_clock(source_clock: &CMClock) -> Result<Self, i32> {
27        extern "C" {
28            fn CMTimebaseCreateWithSourceClock(
29                allocator: *const c_void,
30                sourceClock: *const c_void,
31                timebaseOut: *mut *const c_void,
32            ) -> i32;
33        }
34        let mut ptr = std::ptr::null();
35        let status = unsafe {
36            CMTimebaseCreateWithSourceClock(std::ptr::null(), source_clock.as_ptr(), &raw mut ptr)
37        };
38        if status == 0 && !ptr.is_null() {
39            Ok(Self { ptr })
40        } else {
41            Err(status)
42        }
43    }
44
45    /// Adopts a +1 retained `CMTimebaseRef` and returns `None` for null.
46    ///
47    /// # Safety
48    ///
49    /// A non-null `ptr` must be a live `CMTimebaseRef` of the exact type
50    /// carrying one retain transferred to this wrapper. The caller must not
51    /// release or separately adopt that transferred retain.
52    #[must_use]
53    pub unsafe fn from_raw(ptr: *const c_void) -> Option<Self> {
54        if ptr.is_null() {
55            None
56        } else {
57            Some(Self { ptr })
58        }
59    }
60
61    /// Retains a +0 borrowed `CMTimebaseRef` and returns an owned wrapper.
62    ///
63    /// # Safety
64    ///
65    /// A non-null `ptr` must be a live `CMTimebaseRef` of the exact type for
66    /// the duration of the retain call.
67    #[must_use]
68    pub unsafe fn from_raw_borrowed(ptr: *const c_void) -> Option<Self> {
69        if ptr.is_null() {
70            None
71        } else {
72            extern "C" {
73                fn CFRetain(cf: *const c_void) -> *const c_void;
74            }
75            let retained = unsafe { CFRetain(ptr) };
76            unsafe { Self::from_raw(retained) }
77        }
78    }
79
80    /// Borrow the underlying +0 `CMTimebaseRef` while `self` remains alive.
81    #[must_use]
82    pub const fn as_ptr(&self) -> *const c_void {
83        self.ptr
84    }
85
86    /// Current time.
87    #[must_use]
88    pub fn time(&self) -> CMTime {
89        extern "C" {
90            fn CMTimebaseGetTime(timebase: *const c_void) -> CMTime;
91        }
92        unsafe { CMTimebaseGetTime(self.ptr) }
93    }
94
95    /// Set the current time.
96    #[must_use]
97    pub fn set_time(&self, time: CMTime) -> i32 {
98        extern "C" {
99            fn CMTimebaseSetTime(timebase: *const c_void, time: CMTime) -> i32;
100        }
101        unsafe { CMTimebaseSetTime(self.ptr, time) }
102    }
103
104    /// Playback rate relative to the source clock.
105    #[must_use]
106    pub fn rate(&self) -> f64 {
107        extern "C" {
108            fn CMTimebaseGetRate(timebase: *const c_void) -> f64;
109        }
110        unsafe { CMTimebaseGetRate(self.ptr) }
111    }
112
113    /// Set the playback rate relative to the source clock.
114    #[must_use]
115    pub fn set_rate(&self, rate: f64) -> i32 {
116        extern "C" {
117            fn CMTimebaseSetRate(timebase: *const c_void, rate: f64) -> i32;
118        }
119        unsafe { CMTimebaseSetRate(self.ptr, rate) }
120    }
121
122    /// Copy the source clock.
123    #[must_use]
124    pub fn source_clock(&self) -> Option<CMClock> {
125        extern "C" {
126            fn CMTimebaseCopySourceClock(timebase: *const c_void) -> *const c_void;
127        }
128        let ptr = unsafe { CMTimebaseCopySourceClock(self.ptr) };
129        unsafe { CMClock::from_raw(ptr) }
130    }
131}
132
133impl Clone for CMTimebase {
134    fn clone(&self) -> Self {
135        extern "C" {
136            fn CFRetain(cf: *const c_void) -> *const c_void;
137        }
138        let ptr = unsafe { CFRetain(self.ptr) };
139        Self { ptr }
140    }
141}
142
143impl Drop for CMTimebase {
144    fn drop(&mut self) {
145        extern "C" {
146            fn CFRelease(cf: *const c_void);
147        }
148        unsafe { CFRelease(self.ptr) };
149    }
150}
151
152impl PartialEq for CMTimebase {
153    fn eq(&self, other: &Self) -> bool {
154        self.ptr == other.ptr
155    }
156}
157
158impl Eq for CMTimebase {}
159
160impl std::hash::Hash for CMTimebase {
161    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
162        self.ptr.hash(state);
163    }
164}
165
166impl fmt::Debug for CMTimebase {
167    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
168        f.debug_struct("CMTimebase")
169            .field("ptr", &self.ptr)
170            .field("time", &self.time())
171            .field("rate", &self.rate())
172            .finish()
173    }
174}
175
176// SAFETY: `CMTimebaseRef` is a Core Foundation type; Apple documents its
177// retain/release as thread-safe.  The wrapper only holds the opaque pointer and
178// delegates all mutations through the Core Media API.
179unsafe impl Send for CMTimebase {}
180unsafe impl Sync for CMTimebase {}