1 | // Take a look at the license at the top of the repository in the LICENSE file. |
2 | |
3 | use std::ffi::CStr; |
4 | |
5 | use glib::{prelude::*, translate::*}; |
6 | |
7 | use 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 | |
13 | impl 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" ] |
222 | pub struct ElementBuilder<'a> { |
223 | name_or_factory: NameOrFactory<'a>, |
224 | properties: smallvec::SmallVec<[(&'a str, ValueOrStr<'a>); 16]>, |
225 | } |
226 | |
227 | #[derive (Copy, Clone)] |
228 | enum NameOrFactory<'a> { |
229 | Name(&'a str), |
230 | Factory(&'a ElementFactory), |
231 | } |
232 | |
233 | enum ValueOrStr<'a> { |
234 | Value(glib::Value), |
235 | Str(&'a str), |
236 | } |
237 | |
238 | impl<'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 | |