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