1 // Copyright (C) 2017 Sebastian Dröge <sebastian@centricular.com>
2 //
3 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
6 // option. This file may not be copied, modified, or distributed
7 // except according to those terms.
8 
9 use glib;
10 #[cfg(any(feature = "v1_16", feature = "dox"))]
11 use glib::prelude::*;
12 use glib::translate::*;
13 use glib::IsA;
14 use glib_sys::{gboolean, gpointer};
15 use gst_sys;
16 use libc::c_void;
17 use std::cmp;
18 use std::ptr;
19 use Clock;
20 use ClockEntryType;
21 use ClockError;
22 use ClockFlags;
23 use ClockReturn;
24 use ClockSuccess;
25 use ClockTime;
26 use ClockTimeDiff;
27 
28 use std::sync::atomic;
29 use std::sync::atomic::AtomicI32;
30 
31 glib_wrapper! {
32     #[derive(Debug, PartialOrd, Ord, PartialEq, Eq, Hash)]
33     pub struct ClockId(Shared<c_void>);
34 
35     match fn {
36         ref => |ptr| gst_sys::gst_clock_id_ref(ptr),
37         unref => |ptr| gst_sys::gst_clock_id_unref(ptr),
38     }
39 }
40 
trampoline_wait_async<F: Fn(&Clock, ClockTime, &ClockId) + Send + 'static>( clock: *mut gst_sys::GstClock, time: gst_sys::GstClockTime, id: gpointer, func: gpointer, ) -> gboolean41 unsafe extern "C" fn trampoline_wait_async<F: Fn(&Clock, ClockTime, &ClockId) + Send + 'static>(
42     clock: *mut gst_sys::GstClock,
43     time: gst_sys::GstClockTime,
44     id: gpointer,
45     func: gpointer,
46 ) -> gboolean {
47     let f: &F = &*(func as *const F);
48     f(
49         &from_glib_borrow(clock),
50         from_glib(time),
51         &from_glib_borrow(id),
52     );
53     glib_sys::GTRUE
54 }
55 
destroy_closure_wait_async< F: Fn(&Clock, ClockTime, &ClockId) + Send + 'static, >( ptr: gpointer, )56 unsafe extern "C" fn destroy_closure_wait_async<
57     F: Fn(&Clock, ClockTime, &ClockId) + Send + 'static,
58 >(
59     ptr: gpointer,
60 ) {
61     Box::<F>::from_raw(ptr as *mut _);
62 }
63 
into_raw_wait_async<F: Fn(&Clock, ClockTime, &ClockId) + Send + 'static>(func: F) -> gpointer64 fn into_raw_wait_async<F: Fn(&Clock, ClockTime, &ClockId) + Send + 'static>(func: F) -> gpointer {
65     #[allow(clippy::type_complexity)]
66     let func: Box<F> = Box::new(func);
67     Box::into_raw(func) as gpointer
68 }
69 
70 impl ClockId {
get_time(&self) -> ClockTime71     pub fn get_time(&self) -> ClockTime {
72         unsafe { from_glib(gst_sys::gst_clock_id_get_time(self.to_glib_none().0)) }
73     }
74 
unschedule(&self)75     pub fn unschedule(&self) {
76         unsafe { gst_sys::gst_clock_id_unschedule(self.to_glib_none().0) }
77     }
78 
wait(&self) -> (Result<ClockSuccess, ClockError>, ClockTimeDiff)79     pub fn wait(&self) -> (Result<ClockSuccess, ClockError>, ClockTimeDiff) {
80         unsafe {
81             let mut jitter = 0;
82             let res: ClockReturn = from_glib(gst_sys::gst_clock_id_wait(
83                 self.to_glib_none().0,
84                 &mut jitter,
85             ));
86             (res.into_result(), jitter)
87         }
88     }
89 
wait_async<F>(&self, func: F) -> Result<ClockSuccess, ClockError> where F: Fn(&Clock, ClockTime, &ClockId) + Send + 'static,90     pub fn wait_async<F>(&self, func: F) -> Result<ClockSuccess, ClockError>
91     where
92         F: Fn(&Clock, ClockTime, &ClockId) + Send + 'static,
93     {
94         let ret: ClockReturn = unsafe {
95             from_glib(gst_sys::gst_clock_id_wait_async(
96                 self.to_glib_none().0,
97                 Some(trampoline_wait_async::<F>),
98                 into_raw_wait_async(func),
99                 Some(destroy_closure_wait_async::<F>),
100             ))
101         };
102         ret.into_result()
103     }
104 
compare_by_time(&self, other: &Self) -> cmp::Ordering105     pub fn compare_by_time(&self, other: &Self) -> cmp::Ordering {
106         unsafe {
107             let res =
108                 gst_sys::gst_clock_id_compare_func(self.to_glib_none().0, other.to_glib_none().0);
109             res.cmp(&0)
110         }
111     }
112 
113     #[cfg(any(feature = "v1_16", feature = "dox"))]
get_clock(&self) -> Option<Clock>114     pub fn get_clock(&self) -> Option<Clock> {
115         unsafe { from_glib_full(gst_sys::gst_clock_id_get_clock(self.to_glib_none().0)) }
116     }
117 
118     #[cfg(any(feature = "v1_16", feature = "dox"))]
uses_clock<P: IsA<Clock>>(&self, clock: &P) -> bool119     pub fn uses_clock<P: IsA<Clock>>(&self, clock: &P) -> bool {
120         unsafe {
121             from_glib(gst_sys::gst_clock_id_uses_clock(
122                 self.to_glib_none().0,
123                 clock.as_ref().as_ptr(),
124             ))
125         }
126     }
127 
get_type(&self) -> ClockEntryType128     pub fn get_type(&self) -> ClockEntryType {
129         unsafe {
130             let ptr: *mut gst_sys::GstClockEntry = self.to_glib_none().0 as *mut _;
131             from_glib((*ptr).type_)
132         }
133     }
134 
get_interval(&self) -> Option<ClockTime>135     pub fn get_interval(&self) -> Option<ClockTime> {
136         if self.get_type() != ClockEntryType::Periodic {
137             return None;
138         }
139 
140         unsafe {
141             let ptr: *mut gst_sys::GstClockEntry = self.to_glib_none().0 as *mut _;
142             Some(from_glib((*ptr).interval))
143         }
144     }
145 
get_status(&self) -> &AtomicClockReturn146     pub fn get_status(&self) -> &AtomicClockReturn {
147         unsafe {
148             let ptr: *mut gst_sys::GstClockEntry = self.to_glib_none().0 as *mut _;
149             &*((&(*ptr).status) as *const i32 as *const AtomicClockReturn)
150         }
151     }
152 }
153 
154 #[repr(C)]
155 #[derive(Debug)]
156 pub struct AtomicClockReturn(AtomicI32);
157 
158 impl AtomicClockReturn {
load(&self) -> ClockReturn159     pub fn load(&self) -> ClockReturn {
160         from_glib(self.0.load(atomic::Ordering::SeqCst))
161     }
162 
store(&self, val: ClockReturn)163     pub fn store(&self, val: ClockReturn) {
164         self.0.store(val.to_glib(), atomic::Ordering::SeqCst)
165     }
166 
swap(&self, val: ClockReturn) -> ClockReturn167     pub fn swap(&self, val: ClockReturn) -> ClockReturn {
168         from_glib(self.0.swap(val.to_glib(), atomic::Ordering::SeqCst))
169     }
170 
compare_and_swap(&self, current: ClockReturn, new: ClockReturn) -> ClockReturn171     pub fn compare_and_swap(&self, current: ClockReturn, new: ClockReturn) -> ClockReturn {
172         from_glib(self.0.compare_and_swap(
173             current.to_glib(),
174             new.to_glib(),
175             atomic::Ordering::SeqCst,
176         ))
177     }
178 }
179 
180 unsafe impl Send for ClockId {}
181 unsafe impl Sync for ClockId {}
182 
183 impl Clock {
adjust_with_calibration( internal_target: ClockTime, cinternal: ClockTime, cexternal: ClockTime, cnum: ClockTime, cdenom: ClockTime, ) -> ClockTime184     pub fn adjust_with_calibration(
185         internal_target: ClockTime,
186         cinternal: ClockTime,
187         cexternal: ClockTime,
188         cnum: ClockTime,
189         cdenom: ClockTime,
190     ) -> ClockTime {
191         unsafe {
192             from_glib(gst_sys::gst_clock_adjust_with_calibration(
193                 ptr::null_mut(),
194                 internal_target.to_glib(),
195                 cinternal.to_glib(),
196                 cexternal.to_glib(),
197                 cnum.to_glib(),
198                 cdenom.to_glib(),
199             ))
200         }
201     }
202 
unadjust_with_calibration( external_target: ClockTime, cinternal: ClockTime, cexternal: ClockTime, cnum: ClockTime, cdenom: ClockTime, ) -> ClockTime203     pub fn unadjust_with_calibration(
204         external_target: ClockTime,
205         cinternal: ClockTime,
206         cexternal: ClockTime,
207         cnum: ClockTime,
208         cdenom: ClockTime,
209     ) -> ClockTime {
210         unsafe {
211             from_glib(gst_sys::gst_clock_unadjust_with_calibration(
212                 ptr::null_mut(),
213                 external_target.to_glib(),
214                 cinternal.to_glib(),
215                 cexternal.to_glib(),
216                 cnum.to_glib(),
217                 cdenom.to_glib(),
218             ))
219         }
220     }
221 }
222 
223 pub trait ClockExtManual: 'static {
new_periodic_id( &self, start_time: ClockTime, interval: ClockTime, ) -> Result<ClockId, glib::BoolError>224     fn new_periodic_id(
225         &self,
226         start_time: ClockTime,
227         interval: ClockTime,
228     ) -> Result<ClockId, glib::BoolError>;
229 
periodic_id_reinit( &self, id: &ClockId, start_time: ClockTime, interval: ClockTime, ) -> Result<(), glib::BoolError>230     fn periodic_id_reinit(
231         &self,
232         id: &ClockId,
233         start_time: ClockTime,
234         interval: ClockTime,
235     ) -> Result<(), glib::BoolError>;
236 
new_single_shot_id(&self, time: ClockTime) -> Result<ClockId, glib::BoolError>237     fn new_single_shot_id(&self, time: ClockTime) -> Result<ClockId, glib::BoolError>;
238 
single_shot_id_reinit(&self, id: &ClockId, time: ClockTime) -> Result<(), glib::BoolError>239     fn single_shot_id_reinit(&self, id: &ClockId, time: ClockTime) -> Result<(), glib::BoolError>;
240 
set_clock_flags(&self, flags: ClockFlags)241     fn set_clock_flags(&self, flags: ClockFlags);
242 
unset_clock_flags(&self, flags: ClockFlags)243     fn unset_clock_flags(&self, flags: ClockFlags);
244 
get_clock_flags(&self) -> ClockFlags245     fn get_clock_flags(&self) -> ClockFlags;
246 }
247 
248 impl<O: IsA<Clock>> ClockExtManual for O {
new_periodic_id( &self, start_time: ClockTime, interval: ClockTime, ) -> Result<ClockId, glib::BoolError>249     fn new_periodic_id(
250         &self,
251         start_time: ClockTime,
252         interval: ClockTime,
253     ) -> Result<ClockId, glib::BoolError> {
254         unsafe {
255             Option::<_>::from_glib_full(gst_sys::gst_clock_new_periodic_id(
256                 self.as_ref().to_glib_none().0,
257                 start_time.to_glib(),
258                 interval.to_glib(),
259             ))
260             .ok_or_else(|| glib_bool_error!("Failed to create new periodic clock id"))
261         }
262     }
263 
periodic_id_reinit( &self, id: &ClockId, start_time: ClockTime, interval: ClockTime, ) -> Result<(), glib::BoolError>264     fn periodic_id_reinit(
265         &self,
266         id: &ClockId,
267         start_time: ClockTime,
268         interval: ClockTime,
269     ) -> Result<(), glib::BoolError> {
270         skip_assert_initialized!();
271         unsafe {
272             let res: bool = from_glib(gst_sys::gst_clock_periodic_id_reinit(
273                 self.as_ref().to_glib_none().0,
274                 id.to_glib_none().0,
275                 start_time.to_glib(),
276                 interval.to_glib(),
277             ));
278             if res {
279                 Ok(())
280             } else {
281                 Err(glib_bool_error!("Failed to reinit periodic clock id"))
282             }
283         }
284     }
285 
new_single_shot_id(&self, time: ClockTime) -> Result<ClockId, glib::BoolError>286     fn new_single_shot_id(&self, time: ClockTime) -> Result<ClockId, glib::BoolError> {
287         unsafe {
288             Option::<_>::from_glib_full(gst_sys::gst_clock_new_single_shot_id(
289                 self.as_ref().to_glib_none().0,
290                 time.to_glib(),
291             ))
292             .ok_or_else(|| glib_bool_error!("Failed to create new single shot clock id"))
293         }
294     }
295 
single_shot_id_reinit(&self, id: &ClockId, time: ClockTime) -> Result<(), glib::BoolError>296     fn single_shot_id_reinit(&self, id: &ClockId, time: ClockTime) -> Result<(), glib::BoolError> {
297         unsafe {
298             let res: bool = from_glib(gst_sys::gst_clock_single_shot_id_reinit(
299                 self.as_ref().to_glib_none().0,
300                 id.to_glib_none().0,
301                 time.to_glib(),
302             ));
303             if res {
304                 Ok(())
305             } else {
306                 Err(glib_bool_error!("Failed to reinit single shot clock id"))
307             }
308         }
309     }
310 
set_clock_flags(&self, flags: ClockFlags)311     fn set_clock_flags(&self, flags: ClockFlags) {
312         unsafe {
313             let ptr: *mut gst_sys::GstObject = self.as_ptr() as *mut _;
314             let _guard = ::utils::MutexGuard::lock(&(*ptr).lock);
315             (*ptr).flags |= flags.to_glib();
316         }
317     }
318 
unset_clock_flags(&self, flags: ClockFlags)319     fn unset_clock_flags(&self, flags: ClockFlags) {
320         unsafe {
321             let ptr: *mut gst_sys::GstObject = self.as_ptr() as *mut _;
322             let _guard = ::utils::MutexGuard::lock(&(*ptr).lock);
323             (*ptr).flags &= !flags.to_glib();
324         }
325     }
326 
get_clock_flags(&self) -> ClockFlags327     fn get_clock_flags(&self) -> ClockFlags {
328         unsafe {
329             let ptr: *mut gst_sys::GstObject = self.as_ptr() as *mut _;
330             let _guard = ::utils::MutexGuard::lock(&(*ptr).lock);
331             from_glib((*ptr).flags)
332         }
333     }
334 }
335 
336 #[cfg(test)]
337 mod tests {
338     use super::super::*;
339     use super::*;
340     use std::sync::mpsc::channel;
341 
342     #[test]
test_wait()343     fn test_wait() {
344         ::init().unwrap();
345 
346         let clock = SystemClock::obtain();
347         let now = clock.get_time();
348         let id = clock.new_single_shot_id(now + 20 * ::MSECOND).unwrap();
349         let (res, _) = id.wait();
350 
351         assert!(res == Ok(ClockSuccess::Ok) || res == Err(ClockError::Early));
352     }
353 
354     #[test]
test_wait_async()355     fn test_wait_async() {
356         ::init().unwrap();
357 
358         let (sender, receiver) = channel();
359 
360         let clock = SystemClock::obtain();
361         let now = clock.get_time();
362         let id = clock.new_single_shot_id(now + 20 * ::MSECOND).unwrap();
363         let res = id.wait_async(move |_, _, _| {
364             sender.send(()).unwrap();
365         });
366 
367         assert!(res == Ok(ClockSuccess::Ok));
368 
369         assert_eq!(receiver.recv(), Ok(()));
370     }
371 }
372