Skip to main content

gstreamer_validate/
action_type.rs

1// Take a look at the license at the top of the repository in the LICENSE file.
2
3use std::{ffi::c_int, ptr};
4
5use crate::{Action, ffi};
6use glib::translate::*;
7
8#[derive(Debug)]
9#[repr(transparent)]
10#[doc(alias = "GstValidateActionParameter")]
11pub struct ActionParameter(pub(crate) ffi::GstValidateActionParameter);
12impl Drop for ActionParameter {
13    fn drop(&mut self) {
14        unsafe {
15            if let Some(free_fn) = self.0.free {
16                (free_fn)(self as *const _ as glib::ffi::gpointer);
17            }
18        }
19    }
20}
21
22fn into_glib_content(mut t: Vec<ActionParameter>) -> *mut ffi::GstValidateActionParameter {
23    skip_assert_initialized!();
24    if t.is_empty() {
25        return ptr::null_mut();
26    }
27
28    unsafe {
29        let size = std::mem::size_of::<ffi::GstValidateActionParameter>() * (t.len() + 1);
30        let v_ptr = glib::ffi::g_malloc0(size) as *mut ffi::GstValidateActionParameter;
31
32        ptr::copy_nonoverlapping(
33            t.as_ptr() as *const ffi::GstValidateActionParameter,
34            v_ptr,
35            t.len(),
36        );
37
38        // C side is owning the memory now
39        t.set_len(0);
40
41        v_ptr
42    }
43}
44
45unsafe extern "C" fn action_parameter_free(param: glib::ffi::gpointer) {
46    unsafe {
47        let param = param as *mut ffi::GstValidateActionParameter;
48
49        glib::ffi::g_free((*param).name as *mut _);
50        glib::ffi::g_free((*param).description as *mut _);
51        glib::ffi::g_free((*param).def as *mut _);
52        glib::ffi::g_free((*param).possible_variables as *mut _);
53        glib::ffi::g_free((*param).types as *mut _);
54    }
55}
56
57pub struct ActionParameterBuilder<'a> {
58    name: &'a str,
59    description: &'a str,
60    possible_variables: Vec<String>,
61    mandatory: bool,
62    default_value: Option<&'a str>,
63    types: Vec<String>,
64}
65
66impl<'a> ActionParameterBuilder<'a> {
67    pub fn new(name: &'a str, description: &'a str) -> Self {
68        skip_assert_initialized!();
69
70        Self {
71            name,
72            description,
73            possible_variables: Default::default(),
74            mandatory: false,
75            default_value: None,
76            types: Default::default(),
77        }
78    }
79
80    // rustdoc-stripper-ignore-next
81    /// The name of the variables that can be used to compute the value of the
82    /// parameter. For example for the start value of a seek action, we will
83    /// accept to take 'duration' which will be replace by the total duration of
84    /// the stream on which the action is executed.
85    pub fn add_possible_variable(mut self, possible_variable: &str) -> Self {
86        self.possible_variables.push(possible_variable.to_owned());
87        self
88    }
89
90    pub fn add_possible_variable_if(self, possible_variable: &str, predicate: bool) -> Self {
91        if predicate {
92            self.add_possible_variable(possible_variable)
93        } else {
94            self
95        }
96    }
97
98    pub fn add_possible_variable_if_some(self, possible_variable: Option<&str>) -> Self {
99        if let Some(possible_variable) = possible_variable {
100            self.add_possible_variable(possible_variable)
101        } else {
102            self
103        }
104    }
105
106    pub fn mandatory(mut self) -> Self {
107        self.mandatory = true;
108        self
109    }
110
111    pub fn default_value(mut self, default_value: &'a str) -> Self {
112        self.default_value = Some(default_value);
113        self
114    }
115
116    pub fn default_value_if(self, default_value: &'a str, predicate: bool) -> Self {
117        if predicate {
118            self.default_value(default_value)
119        } else {
120            self
121        }
122    }
123
124    pub fn default_value_if_some(self, default_value: Option<&'a str>) -> Self {
125        if let Some(default_value) = default_value {
126            self.default_value(default_value)
127        } else {
128            self
129        }
130    }
131
132    // rustdoc-stripper-ignore-next
133    /// The types the parameter can take described as a string.
134    ///
135    /// NOTE: The types should end with `(GstClockTime)` if
136    /// its final type is a GstClockTime, this way it will be processed when
137    /// preparing the actions.
138    pub fn add_type(mut self, types: &str) -> Self {
139        self.types.push(types.to_owned());
140        self
141    }
142
143    pub fn add_type_if(self, types: &str, predicate: bool) -> Self {
144        if predicate {
145            self.add_type(types)
146        } else {
147            self
148        }
149    }
150
151    pub fn add_type_if_some(self, types: Option<&str>) -> Self {
152        if let Some(types) = types {
153            self.add_type(types)
154        } else {
155            self
156        }
157    }
158
159    pub fn build(self) -> ActionParameter {
160        let types = if self.types.is_empty() {
161            ptr::null()
162        } else {
163            self.types.join("\n").to_glib_full()
164        };
165        let possible_variables = if self.possible_variables.is_empty() {
166            ptr::null()
167        } else {
168            self.possible_variables.join("\n").to_glib_full()
169        };
170        ActionParameter(ffi::GstValidateActionParameter {
171            name: self.name.to_glib_full(),
172            description: self.description.to_glib_full(),
173            mandatory: self.mandatory.into_glib(),
174            def: self.default_value.to_glib_full(),
175            possible_variables,
176            types,
177            free: Some(action_parameter_free),
178            _gst_reserved: [ptr::null_mut(); 3],
179        })
180    }
181}
182
183type ActionFunction = dyn Fn(&crate::Scenario, &mut crate::Action) -> Result<crate::ActionSuccess, crate::ActionError>
184    + Sync
185    + Send
186    + 'static;
187
188unsafe extern "C" fn destroy_notify(ptr: glib::ffi::gpointer) {
189    unsafe {
190        let _ = Box::from_raw(ptr as *mut Box<ActionFunction>);
191    }
192}
193
194pub struct ActionTypeBuilder<'a> {
195    type_name: &'a str,
196    implementer_namespace: Option<&'a str>,
197    description: Option<&'a str>,
198    parameters: Vec<ActionParameter>,
199    flags: crate::ActionTypeFlags,
200    function: Box<ActionFunction>,
201}
202
203impl<'a> ActionTypeBuilder<'a> {
204    pub fn new<
205        F: Fn(
206                &crate::Scenario,
207                &mut crate::Action,
208            ) -> Result<crate::ActionSuccess, crate::ActionError>
209            + Send
210            + Sync
211            + 'static,
212    >(
213        type_name: &'a str,
214        func: F,
215    ) -> Self {
216        Self {
217            type_name,
218            implementer_namespace: None,
219            description: None,
220            parameters: Vec::new(),
221            flags: crate::ActionTypeFlags::empty(),
222            function: Box::new(func),
223        }
224    }
225
226    pub fn implementer_namespace(mut self, implementer_namespace: &'a str) -> Self {
227        self.implementer_namespace = Some(implementer_namespace);
228        self
229    }
230
231    pub fn implementer_namespace_if(
232        mut self,
233        implementer_namespace: &'a str,
234        predicate: bool,
235    ) -> Self {
236        if predicate {
237            self.implementer_namespace = Some(implementer_namespace);
238            self
239        } else {
240            self
241        }
242    }
243
244    pub fn implementer_namespace_if_some(self, implementer_namespace: Option<&'a str>) -> Self {
245        if let Some(implementer_namespace) = implementer_namespace {
246            self.implementer_namespace(implementer_namespace)
247        } else {
248            self
249        }
250    }
251
252    pub fn description(mut self, description: &'a str) -> Self {
253        self.description = Some(description);
254        self
255    }
256
257    pub fn description_if(mut self, description: &'a str, predicate: bool) -> Self {
258        if predicate {
259            self.description = Some(description);
260            self
261        } else {
262            self
263        }
264    }
265
266    pub fn description_if_some(self, description: Option<&'a str>) -> Self {
267        if let Some(description) = description {
268            self.description(description)
269        } else {
270            self
271        }
272    }
273
274    pub fn parameter(mut self, parameter: ActionParameter) -> Self {
275        self.parameters.push(parameter);
276        self
277    }
278
279    pub fn parameter_if(mut self, parameter: ActionParameter, predicate: bool) -> Self {
280        if predicate {
281            self.parameters.push(parameter);
282            self
283        } else {
284            self
285        }
286    }
287
288    pub fn parameter_if_some(self, parameter: Option<ActionParameter>) -> Self {
289        if let Some(parameter) = parameter {
290            self.parameter(parameter)
291        } else {
292            self
293        }
294    }
295
296    pub fn flags(mut self, flags: crate::ActionTypeFlags) -> Self {
297        self.flags |= flags;
298        self
299    }
300
301    pub fn flags_if(mut self, flags: crate::ActionTypeFlags, predicate: bool) -> Self {
302        if predicate {
303            self.flags |= flags;
304            self
305        } else {
306            self
307        }
308    }
309
310    pub fn flags_if_some(self, flags: Option<crate::ActionTypeFlags>) -> Self {
311        if let Some(flags) = flags {
312            self.flags(flags)
313        } else {
314            self
315        }
316    }
317
318    pub fn build(self) -> crate::ActionType {
319        static QUARK_ACTION_TYPE_FUNC: std::sync::OnceLock<glib::Quark> =
320            std::sync::OnceLock::new();
321
322        let quark_action_type_func =
323            QUARK_ACTION_TYPE_FUNC.get_or_init(|| glib::Quark::from_str("rs-action-type-function"));
324
325        unsafe extern "C" fn execute_func_trampoline(
326            scenario: *mut ffi::GstValidateScenario,
327            mut action_ptr: *mut ffi::GstValidateAction,
328        ) -> c_int {
329            unsafe {
330                let action_type = ffi::gst_validate_get_action_type((*action_ptr).type_);
331                let scenario = from_glib_borrow(scenario);
332
333                let func: &ActionFunction = &*(gst::ffi::gst_mini_object_get_qdata(
334                    action_type as *mut gst::ffi::GstMiniObject,
335                    QUARK_ACTION_TYPE_FUNC.get().unwrap().into_glib(),
336                ) as *const Box<ActionFunction>);
337
338                // SAFETY: `execute_func_trampoline` is called with the unic reference of `action_ptr`
339                // so we can safely borrow it mutably
340                let original_ptr = action_ptr;
341                let action = Action::from_glib_ptr_borrow_mut(&mut action_ptr);
342                let res = (*func)(&scenario, action);
343
344                debug_assert_eq!(action.as_ptr(), original_ptr);
345                match res {
346                    Err(crate::ActionError::Error(ref err)) => {
347                        action.report_error(err);
348                        ffi::GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED
349                    }
350                    Ok(v) => v.into_glib(),
351                }
352            }
353        }
354
355        unsafe {
356            let params = into_glib_content(self.parameters);
357            let action_type = ffi::gst_validate_register_action_type(
358                self.type_name.to_glib_none().0,
359                self.implementer_namespace
360                    .unwrap_or("validaters")
361                    .to_glib_none()
362                    .0,
363                Some(execute_func_trampoline),
364                params,
365                self.description.to_glib_none().0,
366                self.flags.into_glib(),
367            );
368
369            // gst_validate_register_action_type() takes ownership of the content
370            // of the params array but not of the container itself so we need to
371            // free it manually.
372            glib::ffi::g_free(params as *mut _);
373
374            let f = self.function;
375
376            gst::ffi::gst_mini_object_set_qdata(
377                action_type as *mut gst::ffi::GstMiniObject,
378                quark_action_type_func.into_glib(),
379                Box::into_raw(Box::new(f)) as *mut _,
380                Some(destroy_notify),
381            );
382
383            from_glib_none(action_type)
384        }
385    }
386}
387
388pub trait ActionTypeExtManual: 'static {
389    #[doc(alias = "gst_validate_get_action_type")]
390    fn find(name: &str) -> Option<crate::ActionType>;
391}
392
393impl ActionTypeExtManual for crate::ActionType {
394    fn find(name: &str) -> Option<crate::ActionType> {
395        assert_initialized_main_thread!();
396        unsafe {
397            let action_type = ffi::gst_validate_get_action_type(name.to_glib_none().0);
398
399            if action_type.is_null() {
400                None
401            } else {
402                Some(from_glib_full(action_type))
403            }
404        }
405    }
406}