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