gstreamer/subclass/
device_provider.rs

1// Take a look at the license at the top of the repository in the LICENSE file.
2
3use std::borrow::Cow;
4
5use glib::{prelude::*, subclass::prelude::*, translate::*};
6
7use super::prelude::*;
8use crate::{ffi, Device, DeviceProvider, LoggableError};
9
10#[derive(Debug, Clone)]
11pub struct DeviceProviderMetadata {
12    long_name: Cow<'static, str>,
13    classification: Cow<'static, str>,
14    description: Cow<'static, str>,
15    author: Cow<'static, str>,
16    additional: Cow<'static, [(Cow<'static, str>, Cow<'static, str>)]>,
17}
18
19impl DeviceProviderMetadata {
20    pub fn new(long_name: &str, classification: &str, description: &str, author: &str) -> Self {
21        Self {
22            long_name: Cow::Owned(long_name.into()),
23            classification: Cow::Owned(classification.into()),
24            description: Cow::Owned(description.into()),
25            author: Cow::Owned(author.into()),
26            additional: Cow::Borrowed(&[]),
27        }
28    }
29
30    pub fn with_additional(
31        long_name: &str,
32        classification: &str,
33        description: &str,
34        author: &str,
35        additional: &[(&str, &str)],
36    ) -> Self {
37        Self {
38            long_name: Cow::Owned(long_name.into()),
39            classification: Cow::Owned(classification.into()),
40            description: Cow::Owned(description.into()),
41            author: Cow::Owned(author.into()),
42            additional: additional
43                .iter()
44                .copied()
45                .map(|(key, value)| (Cow::Owned(key.into()), Cow::Owned(value.into())))
46                .collect(),
47        }
48    }
49
50    pub const fn with_cow(
51        long_name: Cow<'static, str>,
52        classification: Cow<'static, str>,
53        description: Cow<'static, str>,
54        author: Cow<'static, str>,
55        additional: Cow<'static, [(Cow<'static, str>, Cow<'static, str>)]>,
56    ) -> Self {
57        Self {
58            long_name,
59            classification,
60            description,
61            author,
62            additional,
63        }
64    }
65}
66
67pub trait DeviceProviderImpl: DeviceProviderImplExt + GstObjectImpl + Send + Sync {
68    fn metadata() -> Option<&'static DeviceProviderMetadata> {
69        None
70    }
71
72    /// Returns a list of devices that are currently available.
73    ///  This should never block. The devices should not have a parent and should
74    ///  be floating.
75    fn probe(&self) -> Vec<Device> {
76        self.parent_probe()
77    }
78
79    /// Starts providering the devices. This will cause `GST_MESSAGE_DEVICE_ADDED`
80    /// and `GST_MESSAGE_DEVICE_REMOVED` messages to be posted on the provider's bus
81    /// when devices are added or removed from the system.
82    ///
83    /// Since the [`DeviceProvider`][crate::DeviceProvider] is a singleton,
84    /// [`DeviceProviderExt::start()`][crate::prelude::DeviceProviderExt::start()] may already have been called by another
85    /// user of the object, [`DeviceProviderExt::stop()`][crate::prelude::DeviceProviderExt::stop()] needs to be called the same
86    /// number of times.
87    ///
88    /// After this function has been called, [`DeviceProviderExtManual::devices()`][crate::prelude::DeviceProviderExtManual::devices()] will
89    /// return the same objects that have been received from the
90    /// `GST_MESSAGE_DEVICE_ADDED` messages and will no longer probe.
91    ///
92    /// # Returns
93    ///
94    /// [`true`] if the device providering could be started
95    fn start(&self) -> Result<(), LoggableError> {
96        self.parent_start()
97    }
98
99    /// Decreases the use-count by one. If the use count reaches zero, this
100    /// [`DeviceProvider`][crate::DeviceProvider] will stop providering the devices. This needs to be
101    /// called the same number of times that [`DeviceProviderExt::start()`][crate::prelude::DeviceProviderExt::start()] was called.
102    fn stop(&self) {
103        self.parent_stop()
104    }
105}
106
107mod sealed {
108    pub trait Sealed {}
109    impl<T: super::DeviceProviderImplExt> Sealed for T {}
110}
111
112pub trait DeviceProviderImplExt: sealed::Sealed + ObjectSubclass {
113    fn parent_probe(&self) -> Vec<Device> {
114        unsafe {
115            let data = Self::type_data();
116            let parent_class = data.as_ref().parent_class() as *mut ffi::GstDeviceProviderClass;
117            if let Some(f) = (*parent_class).probe {
118                FromGlibPtrContainer::from_glib_full(f(self
119                    .obj()
120                    .unsafe_cast_ref::<DeviceProvider>()
121                    .to_glib_none()
122                    .0))
123            } else {
124                Vec::new()
125            }
126        }
127    }
128
129    fn parent_start(&self) -> Result<(), LoggableError> {
130        unsafe {
131            let data = Self::type_data();
132            let parent_class = data.as_ref().parent_class() as *mut ffi::GstDeviceProviderClass;
133            let f = (*parent_class).start.ok_or_else(|| {
134                loggable_error!(crate::CAT_RUST, "Parent function `start` is not defined")
135            })?;
136            result_from_gboolean!(
137                f(self
138                    .obj()
139                    .unsafe_cast_ref::<DeviceProvider>()
140                    .to_glib_none()
141                    .0),
142                crate::CAT_RUST,
143                "Failed to start the device provider using the parent function"
144            )
145        }
146    }
147
148    fn parent_stop(&self) {
149        unsafe {
150            let data = Self::type_data();
151            let parent_class = data.as_ref().parent_class() as *mut ffi::GstDeviceProviderClass;
152            if let Some(f) = (*parent_class).stop {
153                f(self
154                    .obj()
155                    .unsafe_cast_ref::<DeviceProvider>()
156                    .to_glib_none()
157                    .0);
158            }
159        }
160    }
161}
162
163impl<T: DeviceProviderImpl> DeviceProviderImplExt for T {}
164
165unsafe impl<T: DeviceProviderImpl> IsSubclassable<T> for DeviceProvider {
166    fn class_init(klass: &mut glib::Class<Self>) {
167        Self::parent_class_init::<T>(klass);
168        let klass = klass.as_mut();
169        klass.probe = Some(device_provider_probe::<T>);
170        klass.start = Some(device_provider_start::<T>);
171        klass.stop = Some(device_provider_stop::<T>);
172
173        unsafe {
174            if let Some(metadata) = T::metadata() {
175                ffi::gst_device_provider_class_set_metadata(
176                    klass,
177                    metadata.long_name.to_glib_none().0,
178                    metadata.classification.to_glib_none().0,
179                    metadata.description.to_glib_none().0,
180                    metadata.author.to_glib_none().0,
181                );
182
183                for (key, value) in metadata.additional.iter() {
184                    ffi::gst_device_provider_class_add_metadata(
185                        klass,
186                        key.to_glib_none().0,
187                        value.to_glib_none().0,
188                    );
189                }
190            }
191        }
192    }
193}
194
195unsafe extern "C" fn device_provider_probe<T: DeviceProviderImpl>(
196    ptr: *mut ffi::GstDeviceProvider,
197) -> *mut glib::ffi::GList {
198    let instance = &*(ptr as *mut T::Instance);
199    let imp = instance.imp();
200
201    imp.probe().to_glib_full()
202}
203
204unsafe extern "C" fn device_provider_start<T: DeviceProviderImpl>(
205    ptr: *mut ffi::GstDeviceProvider,
206) -> glib::ffi::gboolean {
207    let instance = &*(ptr as *mut T::Instance);
208    let imp = instance.imp();
209
210    match imp.start() {
211        Ok(()) => true,
212        Err(err) => {
213            err.log_with_imp(imp);
214            false
215        }
216    }
217    .into_glib()
218}
219
220unsafe extern "C" fn device_provider_stop<T: DeviceProviderImpl>(ptr: *mut ffi::GstDeviceProvider) {
221    let instance = &*(ptr as *mut T::Instance);
222    let imp = instance.imp();
223
224    imp.stop();
225}