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::{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 fn probe(&self) -> Vec<Device> {
73 self.parent_probe()
74 }
75
76 fn start(&self) -> Result<(), LoggableError> {
77 self.parent_start()
78 }
79
80 fn stop(&self) {
81 self.parent_stop()
82 }
83}
84
85mod sealed {
86 pub trait Sealed {}
87 impl<T: super::DeviceProviderImplExt> Sealed for T {}
88}
89
90pub trait DeviceProviderImplExt: sealed::Sealed + ObjectSubclass {
91 fn parent_probe(&self) -> Vec<Device> {
92 unsafe {
93 let data = Self::type_data();
94 let parent_class = data.as_ref().parent_class() as *mut ffi::GstDeviceProviderClass;
95 if let Some(f) = (*parent_class).probe {
96 FromGlibPtrContainer::from_glib_full(f(self
97 .obj()
98 .unsafe_cast_ref::<DeviceProvider>()
99 .to_glib_none()
100 .0))
101 } else {
102 Vec::new()
103 }
104 }
105 }
106
107 fn parent_start(&self) -> Result<(), LoggableError> {
108 unsafe {
109 let data = Self::type_data();
110 let parent_class = data.as_ref().parent_class() as *mut ffi::GstDeviceProviderClass;
111 let f = (*parent_class).start.ok_or_else(|| {
112 loggable_error!(crate::CAT_RUST, "Parent function `start` is not defined")
113 })?;
114 result_from_gboolean!(
115 f(self
116 .obj()
117 .unsafe_cast_ref::<DeviceProvider>()
118 .to_glib_none()
119 .0),
120 crate::CAT_RUST,
121 "Failed to start the device provider using the parent function"
122 )
123 }
124 }
125
126 fn parent_stop(&self) {
127 unsafe {
128 let data = Self::type_data();
129 let parent_class = data.as_ref().parent_class() as *mut ffi::GstDeviceProviderClass;
130 if let Some(f) = (*parent_class).stop {
131 f(self
132 .obj()
133 .unsafe_cast_ref::<DeviceProvider>()
134 .to_glib_none()
135 .0);
136 }
137 }
138 }
139}
140
141impl<T: DeviceProviderImpl> DeviceProviderImplExt for T {}
142
143unsafe impl<T: DeviceProviderImpl> IsSubclassable<T> for DeviceProvider {
144 fn class_init(klass: &mut glib::Class<Self>) {
145 Self::parent_class_init::<T>(klass);
146 let klass = klass.as_mut();
147 klass.probe = Some(device_provider_probe::<T>);
148 klass.start = Some(device_provider_start::<T>);
149 klass.stop = Some(device_provider_stop::<T>);
150
151 unsafe {
152 if let Some(metadata) = T::metadata() {
153 ffi::gst_device_provider_class_set_metadata(
154 klass,
155 metadata.long_name.to_glib_none().0,
156 metadata.classification.to_glib_none().0,
157 metadata.description.to_glib_none().0,
158 metadata.author.to_glib_none().0,
159 );
160
161 for (key, value) in metadata.additional.iter() {
162 ffi::gst_device_provider_class_add_metadata(
163 klass,
164 key.to_glib_none().0,
165 value.to_glib_none().0,
166 );
167 }
168 }
169 }
170 }
171}
172
173unsafe extern "C" fn device_provider_probe<T: DeviceProviderImpl>(
174 ptr: *mut ffi::GstDeviceProvider,
175) -> *mut glib::ffi::GList {
176 let instance: &::Instance = &*(ptr as *mut T::Instance);
177 let imp: &T = instance.imp();
178
179 imp.probe().to_glib_full()
180}
181
182unsafe extern "C" fn device_provider_start<T: DeviceProviderImpl>(
183 ptr: *mut ffi::GstDeviceProvider,
184) -> glib::ffi::gboolean {
185 let instance: &::Instance = &*(ptr as *mut T::Instance);
186 let imp: &T = instance.imp();
187
188 matchbool imp.start() {
189 Ok(()) => true,
190 Err(err: LoggableError) => {
191 err.log_with_imp(imp);
192 false
193 }
194 }
195 .into_glib()
196}
197
198unsafe extern "C" fn device_provider_stop<T: DeviceProviderImpl>(ptr: *mut ffi::GstDeviceProvider) {
199 let instance: &::Instance = &*(ptr as *mut T::Instance);
200 let imp: &T = instance.imp();
201
202 imp.stop();
203}
204