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 | 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 | |
13 | impl 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" ] |
191 | pub struct ElementBuilder<'a> { |
192 | name_or_factory: NameOrFactory<'a>, |
193 | properties: smallvec::SmallVec<[(&'a str, ValueOrStr<'a>); 16]>, |
194 | } |
195 | |
196 | #[derive (Copy, Clone)] |
197 | enum NameOrFactory<'a> { |
198 | Name(&'a str), |
199 | Factory(&'a ElementFactory), |
200 | } |
201 | |
202 | enum ValueOrStr<'a> { |
203 | Value(glib::Value), |
204 | Str(&'a str), |
205 | } |
206 | |
207 | impl<'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 | } |
433 | |