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