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