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