gst_plugin/
element.rs

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
9use std::any::Any;
10use std::mem;
11use std::ptr;
12
13use libc;
14
15use glib_ffi;
16use gobject_ffi;
17use gst_ffi;
18
19use glib;
20use glib::translate::*;
21use gst;
22use gst::prelude::*;
23
24use gobject_subclass::anyimpl::*;
25use gobject_subclass::object::*;
26
27use object::*;
28
29pub trait ElementImpl<T: ElementBase>: ObjectImpl<T> + AnyImpl + Send + Sync + 'static
30where
31    T::InstanceStructType: PanicPoison,
32{
33    fn change_state(&self, element: &T, transition: gst::StateChange) -> gst::StateChangeReturn {
34        element.parent_change_state(transition)
35    }
36
37    fn request_new_pad(
38        &self,
39        _element: &T,
40        _templ: &gst::PadTemplate,
41        _name: Option<String>,
42        _caps: Option<&gst::CapsRef>,
43    ) -> Option<gst::Pad> {
44        None
45    }
46
47    fn release_pad(&self, _element: &T, _pad: &gst::Pad) {}
48
49    fn send_event(&self, element: &T, event: gst::Event) -> bool {
50        element.parent_send_event(event)
51    }
52
53    fn query(&self, element: &T, query: &mut gst::QueryRef) -> bool {
54        element.parent_query(query)
55    }
56
57    fn set_context(&self, element: &T, context: &gst::Context) {
58        element.parent_set_context(context)
59    }
60}
61
62pub trait ElementImplExt<T> {
63    fn catch_panic_pad_function<R, F: FnOnce(&Self, &T) -> R, G: FnOnce() -> R>(
64        parent: &Option<gst::Object>,
65        fallback: G,
66        f: F,
67    ) -> R;
68}
69
70impl<S: ElementImpl<T>, T: ObjectType + glib::IsA<gst::Element> + glib::IsA<gst::Object>>
71    ElementImplExt<T> for S
72where
73    T::InstanceStructType: PanicPoison,
74{
75    fn catch_panic_pad_function<R, F: FnOnce(&Self, &T) -> R, G: FnOnce() -> R>(
76        parent: &Option<gst::Object>,
77        fallback: G,
78        f: F,
79    ) -> R {
80        // FIXME: Does this work for element subclasses?
81        let element = parent.as_ref().unwrap().downcast_ref::<T>().unwrap();
82        let imp = element.get_impl();
83        let imp = Any::downcast_ref::<Box<ElementImpl<T> + 'static>>(imp).unwrap();
84        let imp = imp.downcast_ref::<S>().unwrap();
85        element.catch_panic(fallback, |element| f(imp, element))
86    }
87}
88
89any_impl!(ElementBase, ElementImpl, PanicPoison);
90
91pub unsafe trait ElementBase: IsA<gst::Element> + ObjectType
92where
93    Self::InstanceStructType: PanicPoison,
94{
95    fn parent_change_state(&self, transition: gst::StateChange) -> gst::StateChangeReturn {
96        unsafe {
97            let klass = self.get_class();
98            let parent_klass = (*klass).get_parent_class() as *const gst_ffi::GstElementClass;
99            (*parent_klass)
100                .change_state
101                .map(|f| from_glib(f(self.to_glib_none().0, transition.to_glib())))
102                .unwrap_or(gst::StateChangeReturn::Success)
103        }
104    }
105
106    fn parent_send_event(&self, event: gst::Event) -> bool {
107        unsafe {
108            let klass = self.get_class();
109            let parent_klass = (*klass).get_parent_class() as *const gst_ffi::GstElementClass;
110            (*parent_klass)
111                .send_event
112                .map(|f| from_glib(f(self.to_glib_none().0, event.into_ptr())))
113                .unwrap_or(false)
114        }
115    }
116
117    fn parent_query(&self, query: &mut gst::QueryRef) -> bool {
118        unsafe {
119            let klass = self.get_class();
120            let parent_klass = (*klass).get_parent_class() as *const gst_ffi::GstElementClass;
121            (*parent_klass)
122                .query
123                .map(|f| from_glib(f(self.to_glib_none().0, query.as_mut_ptr())))
124                .unwrap_or(false)
125        }
126    }
127
128    fn parent_set_context(&self, context: &gst::Context) {
129        unsafe {
130            let klass = self.get_class();
131            let parent_klass = (*klass).get_parent_class() as *const gst_ffi::GstElementClass;
132            (*parent_klass)
133                .set_context
134                .map(|f| f(self.to_glib_none().0, context.to_glib_none().0))
135                .unwrap_or(())
136        }
137    }
138
139    fn catch_panic<T, F: FnOnce(&Self) -> T, G: FnOnce() -> T>(&self, fallback: G, f: F) -> T {
140        let panicked = unsafe { &(*self.get_instance()).panicked() };
141        panic_to_error!(self, panicked, fallback(), { f(self) })
142    }
143}
144
145pub unsafe trait ElementClassExt<T: ElementBase>
146where
147    T::ImplType: ElementImpl<T>,
148    T::InstanceStructType: PanicPoison,
149{
150    fn add_pad_template(&mut self, pad_template: gst::PadTemplate) {
151        unsafe {
152            gst_ffi::gst_element_class_add_pad_template(
153                self as *const Self as *mut gst_ffi::GstElementClass,
154                pad_template.to_glib_none().0,
155            );
156        }
157    }
158
159    fn set_metadata(
160        &mut self,
161        long_name: &str,
162        classification: &str,
163        description: &str,
164        author: &str,
165    ) {
166        unsafe {
167            gst_ffi::gst_element_class_set_metadata(
168                self as *const Self as *mut gst_ffi::GstElementClass,
169                long_name.to_glib_none().0,
170                classification.to_glib_none().0,
171                description.to_glib_none().0,
172                author.to_glib_none().0,
173            );
174        }
175    }
176
177    fn override_vfuncs(&mut self, _: &ClassInitToken) {
178        unsafe {
179            let klass = &mut *(self as *const Self as *mut gst_ffi::GstElementClass);
180            klass.change_state = Some(element_change_state::<T>);
181            klass.request_new_pad = Some(element_request_new_pad::<T>);
182            klass.release_pad = Some(element_release_pad::<T>);
183            klass.send_event = Some(element_send_event::<T>);
184            klass.query = Some(element_query::<T>);
185            klass.set_context = Some(element_set_context::<T>);
186        }
187    }
188}
189
190glib_wrapper! {
191    pub struct Element(Object<ElementInstanceStruct<Element>>):
192        [gst::Element => gst_ffi::GstElement,
193         gst::Object => gst_ffi::GstObject];
194
195    match fn {
196        get_type => || get_type::<Element>(),
197    }
198}
199
200unsafe impl<T: IsA<gst::Element> + ObjectType> ElementBase for T where
201    Self::InstanceStructType: PanicPoison
202{
203}
204
205pub type ElementClass = ClassStruct<Element>;
206
207// FIXME: Boilerplate
208unsafe impl ElementClassExt<Element> for ElementClass {}
209unsafe impl ObjectClassExt<Element> for ElementClass {}
210
211unsafe impl Send for Element {}
212unsafe impl Sync for Element {}
213
214#[macro_export]
215macro_rules! box_element_impl(
216    ($name:ident) => {
217        box_object_impl!($name, PanicPoison);
218
219        impl<T: ElementBase> ElementImpl<T> for Box<$name<T>>
220        where
221            T::InstanceStructType: PanicPoison
222        {
223            fn change_state(
224                &self,
225                element: &T,
226                transition: gst::StateChange,
227            ) -> gst::StateChangeReturn {
228                let imp: &$name<T> = self.as_ref();
229                imp.change_state(element, transition)
230            }
231
232            fn request_new_pad(&self, element: &T, templ: &gst::PadTemplate, name: Option<String>, caps: Option<&gst::CapsRef>) -> Option<gst::Pad> {
233                let imp: &$name<T> = self.as_ref();
234                imp.request_new_pad(element, templ, name, caps)
235            }
236
237            fn release_pad(&self, element: &T, pad: &gst::Pad) {
238                let imp: &$name<T> = self.as_ref();
239                imp.release_pad(element, pad)
240            }
241
242            fn send_event(&self, element: &T, event: gst::Event) -> bool {
243                let imp: &$name<T> = self.as_ref();
244                imp.send_event(element, event)
245            }
246
247            fn query(&self, element: &T, query: &mut gst::QueryRef) -> bool {
248                let imp: &$name<T> = self.as_ref();
249                ElementImpl::query(imp, element, query)
250            }
251
252            fn set_context(&self, element: &T, context: &gst::Context) {
253                let imp: &$name<T> = self.as_ref();
254                imp.set_context(element, context)
255            }
256        }
257    };
258);
259
260box_element_impl!(ElementImpl);
261
262impl ObjectType for Element {
263    const NAME: &'static str = "RsElement";
264    type ParentType = gst::Element;
265    type ImplType = Box<ElementImpl<Self>>;
266    type InstanceStructType = ElementInstanceStruct<Self>;
267
268    fn class_init(token: &ClassInitToken, klass: &mut ElementClass) {
269        ObjectClassExt::override_vfuncs(klass, token);
270        ElementClassExt::override_vfuncs(klass, token);
271    }
272
273    object_type_fns!();
274}
275
276unsafe extern "C" fn element_change_state<T: ElementBase>(
277    ptr: *mut gst_ffi::GstElement,
278    transition: gst_ffi::GstStateChange,
279) -> gst_ffi::GstStateChangeReturn
280where
281    T::ImplType: ElementImpl<T>,
282    T::InstanceStructType: PanicPoison,
283{
284    floating_reference_guard!(ptr);
285    let element = &*(ptr as *mut T::InstanceStructType);
286    let wrap: T = from_glib_borrow(ptr as *mut T::InstanceStructType);
287    let imp = element.get_impl();
288
289    // *Never* fail downwards state changes, this causes bugs in GStreamer
290    // and leads to crashes and deadlocks.
291    let transition = from_glib(transition);
292    let fallback = match transition {
293        gst::StateChange::PlayingToPaused
294        | gst::StateChange::PausedToReady
295        | gst::StateChange::ReadyToNull => gst::StateChangeReturn::Success,
296        _ => gst::StateChangeReturn::Failure,
297    };
298
299    panic_to_error!(&wrap, &element.panicked(), fallback, {
300        imp.change_state(&wrap, transition)
301    })
302    .to_glib()
303}
304
305unsafe extern "C" fn element_request_new_pad<T: ElementBase>(
306    ptr: *mut gst_ffi::GstElement,
307    templ: *mut gst_ffi::GstPadTemplate,
308    name: *const libc::c_char,
309    caps: *const gst_ffi::GstCaps,
310) -> *mut gst_ffi::GstPad
311where
312    T::ImplType: ElementImpl<T>,
313    T::InstanceStructType: PanicPoison,
314{
315    floating_reference_guard!(ptr);
316    let element = &*(ptr as *mut T::InstanceStructType);
317    let wrap: T = from_glib_borrow(ptr as *mut T::InstanceStructType);
318    let imp = element.get_impl();
319    let caps = if caps.is_null() {
320        None
321    } else {
322        Some(gst::CapsRef::from_ptr(caps))
323    };
324
325    // XXX: This is effectively unsafe but the best we can do
326    // See https://bugzilla.gnome.org/show_bug.cgi?id=791193
327    let pad = panic_to_error!(&wrap, &element.panicked(), None, {
328        imp.request_new_pad(&wrap, &from_glib_borrow(templ), from_glib_none(name), caps)
329    });
330
331    // Ensure that the pad is owned by the element now, if a pad was returned
332    if let Some(ref pad) = pad {
333        assert_eq!(
334            pad.get_parent(),
335            Some(gst::Object::from_glib_borrow(
336                ptr as *mut gst_ffi::GstObject
337            ))
338        );
339    }
340
341    pad.to_glib_none().0
342}
343
344unsafe extern "C" fn element_release_pad<T: ElementBase>(
345    ptr: *mut gst_ffi::GstElement,
346    pad: *mut gst_ffi::GstPad,
347) where
348    T::ImplType: ElementImpl<T>,
349    T::InstanceStructType: PanicPoison,
350{
351    floating_reference_guard!(ptr);
352    let element = &*(ptr as *mut T::InstanceStructType);
353    let wrap: T = from_glib_borrow(ptr as *mut T::InstanceStructType);
354    let imp = element.get_impl();
355
356    panic_to_error!(&wrap, &element.panicked(), (), {
357        imp.release_pad(&wrap, &from_glib_borrow(pad))
358    })
359}
360
361unsafe extern "C" fn element_send_event<T: ElementBase>(
362    ptr: *mut gst_ffi::GstElement,
363    event: *mut gst_ffi::GstEvent,
364) -> glib_ffi::gboolean
365where
366    T::ImplType: ElementImpl<T>,
367    T::InstanceStructType: PanicPoison,
368{
369    floating_reference_guard!(ptr);
370    let element = &*(ptr as *mut T::InstanceStructType);
371    let wrap: T = from_glib_borrow(ptr as *mut T::InstanceStructType);
372    let imp = element.get_impl();
373
374    panic_to_error!(&wrap, &element.panicked(), false, {
375        imp.send_event(&wrap, from_glib_full(event))
376    })
377    .to_glib()
378}
379
380unsafe extern "C" fn element_query<T: ElementBase>(
381    ptr: *mut gst_ffi::GstElement,
382    query: *mut gst_ffi::GstQuery,
383) -> glib_ffi::gboolean
384where
385    T::ImplType: ElementImpl<T>,
386    T::InstanceStructType: PanicPoison,
387{
388    floating_reference_guard!(ptr);
389    let element = &*(ptr as *mut T::InstanceStructType);
390    let wrap: T = from_glib_borrow(ptr as *mut T::InstanceStructType);
391    let imp = element.get_impl();
392    let query = gst::QueryRef::from_mut_ptr(query);
393
394    panic_to_error!(&wrap, &element.panicked(), false, {
395        imp.query(&wrap, query)
396    })
397    .to_glib()
398}
399
400unsafe extern "C" fn element_set_context<T: ElementBase>(
401    ptr: *mut gst_ffi::GstElement,
402    context: *mut gst_ffi::GstContext,
403) where
404    T::ImplType: ElementImpl<T>,
405    T::InstanceStructType: PanicPoison,
406{
407    floating_reference_guard!(ptr);
408    let element = &*(ptr as *mut T::InstanceStructType);
409    let wrap: T = from_glib_borrow(ptr as *mut T::InstanceStructType);
410    let imp = element.get_impl();
411
412    panic_to_error!(&wrap, &element.panicked(), (), {
413        imp.set_context(&wrap, &from_glib_borrow(context))
414    })
415}