1// Take a look at the license at the top of the repository in the LICENSE file.
2
3use std::{ptr, slice};
4
5use glib::translate::*;
6
7use crate::{Caps, Plugin, Rank, TypeFindFactory, TypeFindProbability};
8
9#[repr(transparent)]
10#[derive(Debug)]
11#[doc(alias = "GstTypeFind")]
12pub struct TypeFind(ffi::GstTypeFind);
13
14pub trait TypeFindImpl {
15 fn peek(&mut self, offset: i64, size: u32) -> Option<&[u8]>;
16 fn suggest(&mut self, probability: TypeFindProbability, caps: &Caps);
17 #[doc(alias = "get_length")]
18 fn length(&self) -> Option<u64> {
19 None
20 }
21}
22
23impl TypeFind {
24 #[doc(alias = "gst_type_find_register")]
25 pub fn register<F>(
26 plugin: Option<&Plugin>,
27 name: &str,
28 rank: Rank,
29 extensions: Option<&str>,
30 possible_caps: Option<&Caps>,
31 func: F,
32 ) -> Result<(), glib::error::BoolError>
33 where
34 F: Fn(&mut TypeFind) + Send + Sync + 'static,
35 {
36 skip_assert_initialized!();
37 unsafe {
38 let func: Box<F> = Box::new(func);
39 let func = Box::into_raw(func);
40
41 let res = ffi::gst_type_find_register(
42 plugin.to_glib_none().0,
43 name.to_glib_none().0,
44 rank.into_glib() as u32,
45 Some(type_find_trampoline::<F>),
46 extensions.to_glib_none().0,
47 possible_caps.to_glib_none().0,
48 func as *mut _,
49 Some(type_find_closure_drop::<F>),
50 );
51
52 glib::result_from_gboolean!(res, "Failed to register typefind factory")
53 }
54 }
55
56 #[doc(alias = "gst_type_find_peek")]
57 pub fn peek(&mut self, offset: i64, size: u32) -> Option<&[u8]> {
58 unsafe {
59 let data = ffi::gst_type_find_peek(&mut self.0, offset, size);
60 if data.is_null() {
61 None
62 } else if size == 0 {
63 Some(&[])
64 } else {
65 Some(slice::from_raw_parts(data, size as usize))
66 }
67 }
68 }
69
70 #[doc(alias = "gst_type_find_suggest")]
71 pub fn suggest(&mut self, probability: TypeFindProbability, caps: &Caps) {
72 unsafe {
73 ffi::gst_type_find_suggest(
74 &mut self.0,
75 probability.into_glib() as u32,
76 caps.to_glib_none().0,
77 );
78 }
79 }
80
81 #[doc(alias = "get_length")]
82 #[doc(alias = "gst_type_find_get_length")]
83 pub fn length(&mut self) -> Option<u64> {
84 unsafe {
85 let len = ffi::gst_type_find_get_length(&mut self.0);
86 if len == 0 {
87 None
88 } else {
89 Some(len)
90 }
91 }
92 }
93}
94
95impl TypeFindFactory {
96 #[doc(alias = "gst_type_find_factory_call_function")]
97 pub fn call_function<T: TypeFindImpl + ?Sized>(&self, mut find: &mut T) {
98 unsafe {
99 let find_ptr: *mut c_void = &mut find as *mut &mut T as glib::ffi::gpointer;
100 let mut find: GstTypeFind = ffi::GstTypeFind {
101 peek: Some(type_find_peek::<T>),
102 suggest: Some(type_find_suggest::<T>),
103 data: find_ptr,
104 get_length: Some(type_find_get_length::<T>),
105 _gst_reserved: [ptr::null_mut(); 4],
106 };
107
108 ffi::gst_type_find_factory_call_function(self.to_glib_none().0, &mut find)
109 }
110 }
111}
112
113unsafe extern "C" fn type_find_trampoline<F: Fn(&mut TypeFind) + Send + Sync + 'static>(
114 find: *mut ffi::GstTypeFind,
115 user_data: glib::ffi::gpointer,
116) {
117 let func: &F = &*(user_data as *const F);
118 func(&mut *(find as *mut TypeFind));
119}
120
121unsafe extern "C" fn type_find_closure_drop<F: Fn(&mut TypeFind) + Send + Sync + 'static>(
122 data: glib::ffi::gpointer,
123) {
124 let _ = Box::<F>::from_raw(data as *mut _);
125}
126
127unsafe extern "C" fn type_find_peek<T: TypeFindImpl + ?Sized>(
128 data: glib::ffi::gpointer,
129 offset: i64,
130 size: u32,
131) -> *const u8 {
132 let find: &mut &mut T = &mut *(data as *mut &mut T);
133 match find.peek(offset, size) {
134 None => ptr::null(),
135 Some(data: &[u8]) => data.as_ptr(),
136 }
137}
138
139unsafe extern "C" fn type_find_suggest<T: TypeFindImpl + ?Sized>(
140 data: glib::ffi::gpointer,
141 probability: u32,
142 caps: *mut ffi::GstCaps,
143) {
144 let find: &mut &mut T = &mut *(data as *mut &mut T);
145 find.suggest(probability:from_glib(probability as i32), &from_glib_borrow(ptr:caps));
146}
147
148unsafe extern "C" fn type_find_get_length<T: TypeFindImpl + ?Sized>(
149 data: glib::ffi::gpointer,
150) -> u64 {
151 use std::u64;
152
153 let find: &&mut T = &*(data as *mut &mut T);
154 find.length().unwrap_or(default:u64::MAX)
155}
156
157#[derive(Debug)]
158pub struct SliceTypeFind<T: AsRef<[u8]>> {
159 pub probability: Option<TypeFindProbability>,
160 pub caps: Option<Caps>,
161 data: T,
162}
163
164impl<T: AsRef<[u8]>> SliceTypeFind<T> {
165 pub fn new(data: T) -> SliceTypeFind<T> {
166 assert_initialized_main_thread!();
167 SliceTypeFind {
168 probability: None,
169 caps: None,
170 data,
171 }
172 }
173
174 pub fn run(&mut self) {
175 let factories = TypeFindFactory::factories();
176
177 for factory in factories {
178 factory.call_function(self);
179 if let Some(prob) = self.probability {
180 if prob >= TypeFindProbability::Maximum {
181 break;
182 }
183 }
184 }
185 }
186
187 pub fn type_find(data: T) -> (TypeFindProbability, Option<Caps>) {
188 assert_initialized_main_thread!();
189 let mut t = SliceTypeFind {
190 probability: None,
191 caps: None,
192 data,
193 };
194
195 t.run();
196
197 (t.probability.unwrap_or(TypeFindProbability::None), t.caps)
198 }
199}
200
201impl<T: AsRef<[u8]>> TypeFindImpl for SliceTypeFind<T> {
202 fn peek(&mut self, offset: i64, size: u32) -> Option<&[u8]> {
203 let data = self.data.as_ref();
204 let len = data.len();
205
206 let offset = if offset >= 0 {
207 usize::try_from(offset).ok()?
208 } else {
209 let offset = usize::try_from(offset.unsigned_abs()).ok()?;
210 if len < offset {
211 return None;
212 }
213
214 len - offset
215 };
216
217 let size = usize::try_from(size).ok()?;
218 let end_offset = offset.checked_add(size)?;
219 if end_offset <= len {
220 Some(&data[offset..end_offset])
221 } else {
222 None
223 }
224 }
225
226 fn suggest(&mut self, probability: TypeFindProbability, caps: &Caps) {
227 match self.probability {
228 None => {
229 self.probability = Some(probability);
230 self.caps = Some(caps.clone());
231 }
232 Some(old_probability) if old_probability < probability => {
233 self.probability = Some(probability);
234 self.caps = Some(caps.clone());
235 }
236 _ => (),
237 }
238 }
239 fn length(&self) -> Option<u64> {
240 Some(self.data.as_ref().len() as u64)
241 }
242}
243
244#[cfg(test)]
245mod tests {
246 use super::*;
247
248 #[test]
249 fn test_typefind_call_function() {
250 crate::init().unwrap();
251
252 let xml_factory = TypeFindFactory::factories()
253 .into_iter()
254 .find(|f| {
255 f.caps()
256 .map(|c| {
257 c.structure(0)
258 .map(|s| s.name() == "application/xml")
259 .unwrap_or(false)
260 })
261 .unwrap_or(false)
262 })
263 .unwrap();
264
265 let data = b"<?xml version=\"1.0\"?><test>test</test>";
266 let data = &data[..];
267 let mut typefind = SliceTypeFind::new(&data);
268 xml_factory.call_function(&mut typefind);
269
270 assert_eq!(
271 typefind.caps,
272 Some(Caps::builder("application/xml").build())
273 );
274 assert_eq!(typefind.probability, Some(TypeFindProbability::Minimum));
275 }
276
277 #[test]
278 fn test_typefind_register() {
279 crate::init().unwrap();
280
281 TypeFind::register(
282 None,
283 "test_typefind",
284 crate::Rank::Primary,
285 None,
286 Some(&Caps::builder("test/test").build()),
287 |typefind| {
288 assert_eq!(typefind.length(), Some(8));
289 let mut found = false;
290 if let Some(data) = typefind.peek(0, 8) {
291 if data == b"abcdefgh" {
292 found = true;
293 }
294 }
295
296 if found {
297 typefind.suggest(
298 TypeFindProbability::Likely,
299 &Caps::builder("test/test").build(),
300 );
301 }
302 },
303 )
304 .unwrap();
305
306 let data = b"abcdefgh";
307 let data = &data[..];
308 let (probability, caps) = SliceTypeFind::type_find(data);
309
310 assert_eq!(caps, Some(Caps::builder("test/test").build()));
311 assert_eq!(probability, TypeFindProbability::Likely);
312 }
313}
314