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