gstreamer/
element_factory.rs

1// Take a look at the license at the top of the repository in the LICENSE file.
2
3use std::ffi::CStr;
4
5use glib::{prelude::*, translate::*};
6
7use crate::{
8    ffi, CapsRef, Element, ElementFactory, Rank, StaticPadTemplate, ELEMENT_METADATA_AUTHOR,
9    ELEMENT_METADATA_DESCRIPTION, ELEMENT_METADATA_DOC_URI, ELEMENT_METADATA_ICON_NAME,
10    ELEMENT_METADATA_KLASS, ELEMENT_METADATA_LONGNAME,
11};
12
13impl ElementFactory {
14    #[doc(alias = "gst_element_factory_create")]
15    #[doc(alias = "gst_element_factory_create_with_properties")]
16    #[track_caller]
17    pub fn create(&self) -> ElementBuilder<'_> {
18        assert_initialized_main_thread!();
19        ElementBuilder {
20            name_or_factory: NameOrFactory::Factory(self),
21            builder: crate::Object::builder_for_deferred_type(),
22        }
23    }
24
25    #[doc(alias = "gst_element_factory_make")]
26    #[doc(alias = "gst_element_factory_make_with_properties")]
27    #[track_caller]
28    pub fn make(factoryname: &str) -> ElementBuilder<'_> {
29        assert_initialized_main_thread!();
30        ElementBuilder {
31            name_or_factory: NameOrFactory::Name(factoryname),
32            builder: crate::Object::builder_for_deferred_type(),
33        }
34    }
35
36    #[doc(alias = "gst_element_factory_create")]
37    #[track_caller]
38    pub fn create_with_name(&self, name: Option<&str>) -> Result<Element, glib::BoolError> {
39        let mut builder = self.create();
40        if let Some(name) = name {
41            builder = builder.name(name);
42        }
43        builder.build()
44    }
45
46    #[doc(alias = "gst_element_factory_make")]
47    #[track_caller]
48    pub fn make_with_name(
49        factoryname: &str,
50        name: Option<&str>,
51    ) -> Result<Element, glib::BoolError> {
52        skip_assert_initialized!();
53        let mut builder = Self::make(factoryname);
54        if let Some(name) = name {
55            builder = builder.name(name);
56        }
57        builder.build()
58    }
59
60    #[doc(alias = "gst_element_factory_get_static_pad_templates")]
61    #[doc(alias = "get_static_pad_templates")]
62    pub fn static_pad_templates(&self) -> glib::List<StaticPadTemplate> {
63        unsafe {
64            glib::List::from_glib_none(ffi::gst_element_factory_get_static_pad_templates(
65                self.to_glib_none().0,
66            ))
67        }
68    }
69
70    #[doc(alias = "gst_element_factory_list_is_type")]
71    pub fn has_type(&self, type_: crate::ElementFactoryType) -> bool {
72        unsafe {
73            from_glib(ffi::gst_element_factory_list_is_type(
74                self.to_glib_none().0,
75                type_.into_glib(),
76            ))
77        }
78    }
79
80    #[doc(alias = "gst_element_factory_list_get_elements")]
81    pub fn factories_with_type(
82        type_: crate::ElementFactoryType,
83        minrank: Rank,
84    ) -> glib::List<ElementFactory> {
85        assert_initialized_main_thread!();
86        unsafe {
87            FromGlibPtrContainer::from_glib_full(ffi::gst_element_factory_list_get_elements(
88                type_.into_glib(),
89                minrank.into_glib(),
90            ))
91        }
92    }
93
94    #[doc(alias = "gst_element_factory_get_metadata")]
95    #[doc(alias = "get_metadata")]
96    pub fn metadata(&self, key: &str) -> Option<&str> {
97        unsafe {
98            let ptr =
99                ffi::gst_element_factory_get_metadata(self.to_glib_none().0, key.to_glib_none().0);
100
101            if ptr.is_null() {
102                None
103            } else {
104                Some(CStr::from_ptr(ptr).to_str().unwrap())
105            }
106        }
107    }
108
109    #[doc(alias = "get_longname")]
110    #[doc(alias = "gst_element_factory_get_longname")]
111    pub fn longname(&self) -> &str {
112        self.metadata(ELEMENT_METADATA_LONGNAME).unwrap()
113    }
114
115    #[doc(alias = "get_klass")]
116    #[doc(alias = "gst_element_factory_get_klass")]
117    pub fn klass(&self) -> &str {
118        self.metadata(ELEMENT_METADATA_KLASS).unwrap()
119    }
120
121    #[doc(alias = "get_description")]
122    #[doc(alias = "gst_element_factory_get_description")]
123    pub fn description(&self) -> &str {
124        self.metadata(ELEMENT_METADATA_DESCRIPTION).unwrap()
125    }
126
127    #[doc(alias = "get_author")]
128    #[doc(alias = "gst_element_factory_get_author")]
129    pub fn author(&self) -> &str {
130        self.metadata(ELEMENT_METADATA_AUTHOR).unwrap()
131    }
132
133    #[doc(alias = "get_documentation_uri")]
134    #[doc(alias = "gst_element_factory_get_documentation_uri")]
135    pub fn documentation_uri(&self) -> Option<&str> {
136        self.metadata(ELEMENT_METADATA_DOC_URI)
137    }
138
139    #[doc(alias = "get_icon_name")]
140    #[doc(alias = "gst_element_factory_get_icon_name")]
141    pub fn icon_name(&self) -> Option<&str> {
142        self.metadata(ELEMENT_METADATA_ICON_NAME)
143    }
144
145    #[doc(alias = "gst_element_factory_can_sink_all_caps")]
146    pub fn can_sink_all_caps(&self, caps: &CapsRef) -> bool {
147        unsafe {
148            from_glib(ffi::gst_element_factory_can_sink_all_caps(
149                self.to_glib_none().0,
150                caps.as_ptr(),
151            ))
152        }
153    }
154
155    #[doc(alias = "gst_element_factory_can_sink_any_caps")]
156    pub fn can_sink_any_caps(&self, caps: &CapsRef) -> bool {
157        unsafe {
158            from_glib(ffi::gst_element_factory_can_sink_any_caps(
159                self.to_glib_none().0,
160                caps.as_ptr(),
161            ))
162        }
163    }
164
165    #[doc(alias = "gst_element_factory_can_src_all_caps")]
166    pub fn can_src_all_caps(&self, caps: &CapsRef) -> bool {
167        unsafe {
168            from_glib(ffi::gst_element_factory_can_src_all_caps(
169                self.to_glib_none().0,
170                caps.as_ptr(),
171            ))
172        }
173    }
174
175    #[doc(alias = "gst_element_factory_can_src_any_caps")]
176    pub fn can_src_any_caps(&self, caps: &CapsRef) -> bool {
177        unsafe {
178            from_glib(ffi::gst_element_factory_can_src_any_caps(
179                self.to_glib_none().0,
180                caps.as_ptr(),
181            ))
182        }
183    }
184}
185
186// rustdoc-stripper-ignore-next
187/// Builder for `Element`s.
188#[must_use = "The builder must be built to be used"]
189pub struct ElementBuilder<'a> {
190    name_or_factory: NameOrFactory<'a>,
191    builder: crate::gobject::GObjectBuilder<'a, Element>,
192}
193
194#[derive(Copy, Clone)]
195enum NameOrFactory<'a> {
196    Name(&'a str),
197    Factory(&'a ElementFactory),
198}
199
200impl<'a> ElementBuilder<'a> {
201    // rustdoc-stripper-ignore-next
202    /// Sets property `name` to the given value `value`.
203    ///
204    /// Overrides any default or previously defined value for `name`.
205    #[inline]
206    pub fn property(self, name: &'a str, value: impl Into<glib::Value> + 'a) -> Self {
207        Self {
208            builder: self.builder.property(name, value),
209            ..self
210        }
211    }
212
213    // rustdoc-stripper-ignore-next
214    /// Sets property `name` to the given string value `value`.
215    #[inline]
216    pub fn property_from_str(self, name: &'a str, value: &'a str) -> Self {
217        Self {
218            builder: self.builder.property_from_str(name, value),
219            ..self
220        }
221    }
222
223    impl_builder_gvalue_extra_setters!(property_and_name);
224
225    // rustdoc-stripper-ignore-next
226    /// Builds the [`Element`] with the provided properties.
227    ///
228    /// This fails if there is no such [`ElementFactory`] or the [`ElementFactory`] can't be loaded.
229    ///
230    /// # Panics
231    ///
232    /// This panics if the [`Element`] is not instantiable, doesn't have all the given properties or
233    /// property values of the wrong type are provided.
234    ///
235    /// [`Element`]: crate::Element
236    #[track_caller]
237    #[must_use = "Building the element without using it has no effect"]
238    pub fn build(self) -> Result<Element, glib::BoolError> {
239        let mut _factory_found = None;
240        let factory = match self.name_or_factory {
241            NameOrFactory::Name(name) => {
242                let factory = ElementFactory::find(name).ok_or_else(|| {
243                    crate::warning!(crate::CAT_RUST, "element factory '{}' not found", name);
244                    glib::bool_error!(
245                        "Failed to find element factory with name '{}' for creating element",
246                        name
247                    )
248                })?;
249                _factory_found = Some(factory);
250                _factory_found.as_ref().unwrap()
251            }
252            NameOrFactory::Factory(factory) => factory,
253        };
254
255        // The below is basically a reimplementation of the C function. We want to call
256        // glib::Object::with_type() ourselves here for checking properties and their values
257        // correctly and to provide consistent behaviour.
258        use crate::prelude::{
259            ElementExtManual, GstObjectExt, GstObjectExtManual, PluginFeatureExtManual,
260        };
261
262        let factory = factory.load().map_err(|_| {
263            crate::warning!(
264                crate::CAT_RUST,
265                obj = factory,
266                "loading element factory '{}' failed",
267                factory.name(),
268            );
269            glib::bool_error!(
270                "Failed to load element factory '{}' for creating element",
271                factory.name()
272            )
273        })?;
274
275        let element_type = factory.element_type();
276        if !element_type.is_valid() {
277            crate::warning!(
278                crate::CAT_RUST,
279                obj = &factory,
280                "element factory '{}' has no type",
281                factory.name()
282            );
283            return Err(glib::bool_error!(
284                "Failed to create element from factory '{}'",
285                factory.name()
286            ));
287        }
288
289        let element = self
290            .builder
291            .type_(element_type)
292            .build()
293            .map_err(|err| {
294                use crate::gobject::GObjectError::*;
295                match err {
296                    PropertyNotFound { property, .. } => {
297                        format!("property '{property}' of element factory '{}' not found", factory.name())
298                    },
299                    PropertyFromStr { property, value, .. } => {
300                        format!("property '{property}' of element factory '{}' can't be set from string '{value}'", factory.name())
301                    },
302                }
303            }).unwrap();
304
305        unsafe {
306            use std::sync::atomic;
307
308            let klass = element.element_class();
309            let factory_ptr: &atomic::AtomicPtr<ffi::GstElementFactory> =
310                &*(&klass.as_ref().elementfactory as *const *mut ffi::GstElementFactory
311                    as *const atomic::AtomicPtr<ffi::GstElementFactory>);
312            if factory_ptr
313                .compare_exchange(
314                    std::ptr::null_mut(),
315                    factory.as_ptr(),
316                    atomic::Ordering::SeqCst,
317                    atomic::Ordering::SeqCst,
318                )
319                .is_ok()
320            {
321                factory.set_object_flags(crate::ObjectFlags::MAY_BE_LEAKED);
322            }
323
324            if glib::gobject_ffi::g_object_is_floating(factory.as_ptr() as *mut _)
325                != glib::ffi::GFALSE
326            {
327                glib::g_critical!(
328                    "GStreamer",
329                    "The created element should be floating, this is probably caused by faulty bindings",
330                );
331            }
332        }
333
334        crate::log!(
335            crate::CAT_RUST,
336            obj = &factory,
337            "created element \"{}\"",
338            factory.name()
339        );
340
341        Ok(element)
342    }
343}
344
345#[cfg(test)]
346mod tests {
347    use super::*;
348    use crate::prelude::*;
349
350    #[test]
351    fn builder() {
352        crate::init().unwrap();
353
354        let fakesink = ElementFactory::make("fakesink")
355            .name("test-fakesink")
356            .property("can-activate-pull", true)
357            .property_from_str("state-error", "ready-to-paused")
358            .build()
359            .unwrap();
360
361        assert_eq!(fakesink.name(), "test-fakesink");
362        assert!(fakesink.property::<bool>("can-activate-pull"));
363        let v = fakesink.property_value("state-error");
364        let (_klass, e) = glib::EnumValue::from_value(&v).unwrap();
365        assert_eq!(e.nick(), "ready-to-paused");
366    }
367}