Skip to main content

gstreamer/
object.rs

1// Take a look at the license at the top of the repository in the LICENSE file.
2
3use std::ptr;
4#[cfg(feature = "v1_28")]
5#[cfg(feature = "futures")]
6use std::{future::Future, pin::Pin};
7
8use glib::{prelude::*, signal::SignalHandlerId, translate::*};
9
10use crate::{ClockTime, Object, ObjectFlags, ffi};
11
12pub trait GstObjectExtManual: IsA<Object> + 'static {
13    #[doc(alias = "deep-notify")]
14    fn connect_deep_notify<
15        F: Fn(&Self, &crate::Object, &glib::ParamSpec) + Send + Sync + 'static,
16    >(
17        &self,
18        name: Option<&str>,
19        f: F,
20    ) -> SignalHandlerId {
21        let signal_name = if let Some(name) = name {
22            format!("deep-notify::{name}")
23        } else {
24            "deep-notify".into()
25        };
26
27        let obj: Borrowed<glib::Object> =
28            unsafe { from_glib_borrow(self.as_ptr() as *mut glib::gobject_ffi::GObject) };
29
30        obj.connect(signal_name.as_str(), false, move |values| {
31            // It would be nice to display the actual signal name in the panic messages below,
32            // but that would require to copy `signal_name` so as to move it into the closure
33            // which seems too much for the messages of development errors
34            let obj: Self = unsafe {
35                values[0]
36                    .get::<crate::Object>()
37                    .unwrap_or_else(|err| panic!("Object signal \"deep-notify\": values[0]: {err}"))
38                    .unsafe_cast()
39            };
40            let prop_obj: crate::Object = values[1]
41                .get()
42                .unwrap_or_else(|err| panic!("Object signal \"deep-notify\": values[1]: {err}"));
43
44            let pspec = unsafe {
45                let pspec = glib::gobject_ffi::g_value_get_param(values[2].to_glib_none().0);
46                from_glib_none(pspec)
47            };
48
49            f(&obj, &prop_obj, &pspec);
50
51            None
52        })
53    }
54
55    fn set_object_flags(&self, flags: ObjectFlags) {
56        unsafe {
57            let ptr: *mut ffi::GstObject = self.as_ptr() as *mut _;
58            let _guard = self.as_ref().object_lock();
59            (*ptr).flags |= flags.into_glib();
60        }
61    }
62
63    fn unset_object_flags(&self, flags: ObjectFlags) {
64        unsafe {
65            let ptr: *mut ffi::GstObject = self.as_ptr() as *mut _;
66            let _guard = self.as_ref().object_lock();
67            (*ptr).flags &= !flags.into_glib();
68        }
69    }
70
71    #[doc(alias = "get_object_flags")]
72    fn object_flags(&self) -> ObjectFlags {
73        unsafe {
74            let ptr: *mut ffi::GstObject = self.as_ptr() as *mut _;
75            let _guard = self.as_ref().object_lock();
76            from_glib((*ptr).flags)
77        }
78    }
79
80    #[doc(alias = "get_g_value_array")]
81    #[doc(alias = "gst_object_get_g_value_array")]
82    fn g_value_array(
83        &self,
84        property_name: &str,
85        timestamp: ClockTime,
86        interval: ClockTime,
87        values: &mut [glib::Value],
88    ) -> Result<(), glib::error::BoolError> {
89        let n_values = values.len() as u32;
90        unsafe {
91            glib::result_from_gboolean!(
92                ffi::gst_object_get_g_value_array(
93                    self.as_ref().to_glib_none().0,
94                    property_name.to_glib_none().0,
95                    timestamp.into_glib(),
96                    interval.into_glib(),
97                    n_values,
98                    values.as_mut_ptr() as *mut glib::gobject_ffi::GValue,
99                ),
100                "Failed to get value array"
101            )
102        }
103    }
104
105    #[inline]
106    fn object_lock(&self) -> crate::utils::ObjectLockGuard<'_, Self> {
107        crate::utils::ObjectLockGuard::acquire(self)
108    }
109
110    #[cfg(feature = "v1_28")]
111    #[doc(alias = "gst_object_call_async")]
112    fn call_async<F>(&self, func: F)
113    where
114        F: FnOnce(&Self) + Send + 'static,
115    {
116        let user_data: Box<F> = Box::new(func);
117
118        unsafe extern "C" fn trampoline<O: IsA<Object>, F: FnOnce(&O) + Send + 'static>(
119            object: *mut ffi::GstObject,
120            user_data: glib::ffi::gpointer,
121        ) {
122            unsafe {
123                let callback: Box<F> = Box::from_raw(user_data as *mut _);
124                callback(Object::from_glib_borrow(object).unsafe_cast_ref());
125            }
126        }
127
128        unsafe {
129            ffi::gst_object_call_async(
130                self.as_ref().to_glib_none().0,
131                Some(trampoline::<Self, F>),
132                Box::into_raw(user_data) as *mut _,
133            );
134        }
135    }
136
137    #[cfg(feature = "v1_28")]
138    #[cfg(feature = "futures")]
139    fn call_async_future<F, T>(&self, func: F) -> Pin<Box<dyn Future<Output = T> + Send + 'static>>
140    where
141        F: FnOnce(&Self) -> T + Send + 'static,
142        T: Send + 'static,
143    {
144        use futures_channel::oneshot;
145
146        let (sender, receiver) = oneshot::channel();
147
148        self.call_async(move |object| {
149            let _ = sender.send(func(object));
150        });
151
152        Box::pin(async move { receiver.await.expect("sender dropped") })
153    }
154
155    // rustdoc-stripper-ignore-next
156    /// Sets the parent of `self` to `parent`. If `self` already has a parent this will fail.
157    ///
158    /// The returned reference to `self` on success must be kept alive until the `parent` is unset
159    /// again. Also `parent` must be kept alive until `child` gets its parent unset, or in other
160    /// words if `child` doesn't get its parent unset until `parent` is disposed then during
161    /// disposal this must happen. Not doing so causes dangling references and potential
162    /// use-after-frees.
163    #[doc(alias = "gst_object_set_parent")]
164    #[doc(alias = "parent")]
165    unsafe fn set_parent(&self, parent: &impl IsA<Object>) -> Result<Self, glib::error::BoolError> {
166        unsafe {
167            glib::result_from_gboolean!(
168                ffi::gst_object_set_parent(
169                    self.as_ref().to_glib_none().0,
170                    parent.as_ref().to_glib_none().0
171                ),
172                "Failed to set parent object"
173            )
174            .map(|_| {
175                // set_parent() increases the reference count of the child but only on success.
176                Object::from_glib_full(self.as_ref().as_ptr()).unsafe_cast()
177            })
178        }
179    }
180
181    // rustdoc-stripper-ignore-next
182    /// Unsets the parent of `self` from `parent`. If `self` has no parent or a different parent
183    /// than `parent` this will fail.
184    #[doc(alias = "gst_object_set_parent")]
185    #[doc(alias = "parent")]
186    unsafe fn unset_parent(&self, parent: &impl IsA<Object>) -> Result<(), glib::error::BoolError> {
187        unsafe {
188            let _lock = self.object_lock();
189
190            if (*self.as_ref().as_ptr()).parent != parent.as_ref().as_ptr() {
191                return Err(glib::bool_error!("Failed to unset parent object"));
192            }
193
194            (*self.as_ref().as_ptr()).parent = ptr::null_mut();
195
196            Ok(())
197        }
198    }
199}
200
201impl<O: IsA<Object>> GstObjectExtManual for O {}
202
203#[cfg(test)]
204mod tests {
205    use std::sync::{Arc, Mutex};
206
207    use super::*;
208    use crate::prelude::*;
209
210    #[test]
211    fn test_deep_notify() {
212        crate::init().unwrap();
213
214        let bin = crate::Bin::new();
215        let identity = crate::ElementFactory::make("identity")
216            .name("id")
217            .build()
218            .unwrap();
219        bin.add(&identity).unwrap();
220
221        let notify = Arc::new(Mutex::new(None));
222        let notify_clone = notify.clone();
223        bin.connect_deep_notify(None, move |_, id, prop| {
224            *notify_clone.lock().unwrap() = Some((id.clone(), prop.name()));
225        });
226
227        identity.set_property("silent", false);
228        assert_eq!(
229            *notify.lock().unwrap(),
230            Some((identity.upcast::<crate::Object>(), "silent"))
231        );
232    }
233
234    mod test_object {
235        use super::*;
236
237        use glib::subclass::prelude::*;
238
239        pub mod imp {
240            use std::collections::BTreeSet;
241
242            use super::*;
243
244            use crate::subclass::prelude::*;
245
246            #[derive(Default)]
247            pub struct TestObject {
248                pub(super) children: Mutex<BTreeSet<Object>>,
249            }
250
251            #[glib::object_subclass]
252            impl ObjectSubclass for TestObject {
253                const NAME: &'static str = "TestObject";
254                type Type = super::TestObject;
255                type ParentType = crate::Object;
256            }
257
258            impl ObjectImpl for TestObject {
259                fn dispose(&self) {
260                    // Safety: Need to make sure to keep a reference to the child until
261                    // it is removed or the parent goes away
262                    unsafe {
263                        let mut children = self.children.lock().unwrap();
264                        for child in children.iter() {
265                            child.unset_parent(&*self.obj()).unwrap();
266                        }
267                        children.clear();
268                    }
269                }
270            }
271
272            impl GstObjectImpl for TestObject {}
273        }
274
275        glib::wrapper! {
276            pub struct TestObject(ObjectSubclass<imp::TestObject>) @extends crate::Object;
277        }
278
279        impl TestObject {
280            pub fn new() -> Self {
281                glib::Object::builder().build()
282            }
283
284            pub fn add(&self, child: &impl IsA<Object>) -> bool {
285                if child.as_ref() == self.upcast_ref::<Object>() {
286                    return false;
287                }
288
289                let mut children = self.imp().children.lock().unwrap();
290
291                if children.iter().any(|other| other.name() == child.name()) {
292                    return false;
293                }
294
295                // Safety: Need to make sure to keep a reference to the child until
296                // it is removed or the parent goes away
297                unsafe {
298                    if let Ok(child) = child.as_ref().set_parent(self) {
299                        let inserted = children.insert(child);
300                        assert!(inserted);
301                        true
302                    } else {
303                        false
304                    }
305                }
306            }
307
308            pub fn remove(&self, child: &impl IsA<Object>) -> bool {
309                let mut children = self.imp().children.lock().unwrap();
310
311                // Safety: Need to make sure to keep a reference to the child until
312                // it is removed or the parent goes away
313                unsafe {
314                    if child.unset_parent(self).is_ok() {
315                        let found = children.remove(child.as_ref());
316                        assert!(found);
317                        true
318                    } else {
319                        false
320                    }
321                }
322            }
323        }
324    }
325
326    #[test]
327    fn test_set_unset_parent() {
328        crate::init().unwrap();
329
330        let p1 = test_object::TestObject::new();
331        let p2 = test_object::TestObject::new();
332
333        let c1 = test_object::TestObject::new();
334        let c2 = test_object::TestObject::new();
335
336        assert!(p1.add(&c1));
337        assert!(p1.parent().is_none());
338        assert_eq!(c1.parent().as_ref(), Some(p1.upcast_ref()));
339        assert_eq!(p1.ref_count(), 1);
340        assert_eq!(c1.ref_count(), 2);
341
342        assert!(p2.add(&c2));
343        assert!(p2.parent().is_none());
344        assert_eq!(c2.parent().as_ref(), Some(p2.upcast_ref()));
345        assert_eq!(p2.ref_count(), 1);
346        assert_eq!(c2.ref_count(), 2);
347
348        assert!(!p2.add(&c1));
349        assert_eq!(c1.parent().as_ref(), Some(p1.upcast_ref()));
350
351        assert!(p2.remove(&c2));
352        assert_eq!(c2.parent().as_ref(), None::<&Object>);
353        assert_eq!(p2.ref_count(), 1);
354        assert_eq!(c2.ref_count(), 1);
355
356        assert!(p1.add(&c2));
357        assert_eq!(c2.parent().as_ref(), Some(p1.upcast_ref()));
358        assert_eq!(p1.ref_count(), 1);
359        assert_eq!(c2.ref_count(), 2);
360
361        assert!(p1.remove(&c2));
362        assert_eq!(c1.parent().as_ref(), Some(p1.upcast_ref()));
363        assert_eq!(c2.parent().as_ref(), None::<&Object>);
364
365        assert_eq!(p1.ref_count(), 1);
366        assert_eq!(p2.ref_count(), 1);
367        assert_eq!(c1.ref_count(), 2);
368        assert_eq!(c2.ref_count(), 1);
369    }
370}