1use std::{ptr, slice};
4
5use glib::translate::*;
6
7use crate::{ffi, Caps, Plugin, Rank, TypeFindFactory, TypeFindProbability};
8
9#[repr(transparent)]
12#[derive(Debug)]
13#[doc(alias = "GstTypeFind")]
14pub struct TypeFind(ffi::GstTypeFind);
15
16pub trait TypeFindImpl {
17 fn peek(&mut self, offset: i64, size: u32) -> Option<&[u8]>;
18 fn suggest(&mut self, probability: TypeFindProbability, caps: &Caps);
19 #[doc(alias = "get_length")]
20 fn length(&self) -> Option<u64> {
21 None
22 }
23}
24
25impl TypeFind {
26 #[doc(alias = "gst_type_find_register")]
51 pub fn register<F>(
52 plugin: Option<&Plugin>,
53 name: &str,
54 rank: Rank,
55 extensions: Option<&str>,
56 possible_caps: Option<&Caps>,
57 func: F,
58 ) -> Result<(), glib::error::BoolError>
59 where
60 F: Fn(&mut TypeFind) + Send + Sync + 'static,
61 {
62 skip_assert_initialized!();
63 unsafe {
64 let func: Box<F> = Box::new(func);
65 let func = Box::into_raw(func);
66
67 let res = ffi::gst_type_find_register(
68 plugin.to_glib_none().0,
69 name.to_glib_none().0,
70 rank.into_glib() as u32,
71 Some(type_find_trampoline::<F>),
72 extensions.to_glib_none().0,
73 possible_caps.to_glib_none().0,
74 func as *mut _,
75 Some(type_find_closure_drop::<F>),
76 );
77
78 glib::result_from_gboolean!(res, "Failed to register typefind factory")
79 }
80 }
81
82 #[doc(alias = "gst_type_find_peek")]
97 pub fn peek(&mut self, offset: i64, size: u32) -> Option<&[u8]> {
98 unsafe {
99 let data = ffi::gst_type_find_peek(&mut self.0, offset, size);
100 if data.is_null() {
101 None
102 } else if size == 0 {
103 Some(&[])
104 } else {
105 Some(slice::from_raw_parts(data, size as usize))
106 }
107 }
108 }
109
110 #[doc(alias = "gst_type_find_suggest")]
119 pub fn suggest(&mut self, probability: TypeFindProbability, caps: &Caps) {
120 unsafe {
121 ffi::gst_type_find_suggest(
122 &mut self.0,
123 probability.into_glib() as u32,
124 caps.to_glib_none().0,
125 );
126 }
127 }
128
129 #[doc(alias = "get_length")]
135 #[doc(alias = "gst_type_find_get_length")]
136 pub fn length(&mut self) -> Option<u64> {
137 unsafe {
138 let len = ffi::gst_type_find_get_length(&mut self.0);
139 if len == 0 {
140 None
141 } else {
142 Some(len)
143 }
144 }
145 }
146}
147
148impl TypeFindFactory {
149 #[doc(alias = "gst_type_find_factory_call_function")]
154 pub fn call_function<T: TypeFindImpl + ?Sized>(&self, mut find: &mut T) {
155 unsafe {
156 let find_ptr = &mut find as *mut &mut T as glib::ffi::gpointer;
157 let mut find = ffi::GstTypeFind {
158 peek: Some(type_find_peek::<T>),
159 suggest: Some(type_find_suggest::<T>),
160 data: find_ptr,
161 get_length: Some(type_find_get_length::<T>),
162 _gst_reserved: [ptr::null_mut(); 4],
163 };
164
165 ffi::gst_type_find_factory_call_function(self.to_glib_none().0, &mut find)
166 }
167 }
168}
169
170unsafe extern "C" fn type_find_trampoline<F: Fn(&mut TypeFind) + Send + Sync + 'static>(
171 find: *mut ffi::GstTypeFind,
172 user_data: glib::ffi::gpointer,
173) {
174 let func: &F = &*(user_data as *const F);
175 func(&mut *(find as *mut TypeFind));
176}
177
178unsafe extern "C" fn type_find_closure_drop<F: Fn(&mut TypeFind) + Send + Sync + 'static>(
179 data: glib::ffi::gpointer,
180) {
181 let _ = Box::<F>::from_raw(data as *mut _);
182}
183
184unsafe extern "C" fn type_find_peek<T: TypeFindImpl + ?Sized>(
185 data: glib::ffi::gpointer,
186 offset: i64,
187 size: u32,
188) -> *const u8 {
189 let find = &mut *(data as *mut &mut T);
190 match find.peek(offset, size) {
191 None => ptr::null(),
192 Some(data) => data.as_ptr(),
193 }
194}
195
196unsafe extern "C" fn type_find_suggest<T: TypeFindImpl + ?Sized>(
197 data: glib::ffi::gpointer,
198 probability: u32,
199 caps: *mut ffi::GstCaps,
200) {
201 let find = &mut *(data as *mut &mut T);
202 find.suggest(from_glib(probability as i32), &from_glib_borrow(caps));
203}
204
205unsafe extern "C" fn type_find_get_length<T: TypeFindImpl + ?Sized>(
206 data: glib::ffi::gpointer,
207) -> u64 {
208 let find = &*(data as *mut &mut T);
209 find.length().unwrap_or(u64::MAX)
210}
211
212#[derive(Debug)]
213pub struct SliceTypeFind<T: AsRef<[u8]>> {
214 pub probability: Option<TypeFindProbability>,
215 pub caps: Option<Caps>,
216 data: T,
217}
218
219impl<T: AsRef<[u8]>> SliceTypeFind<T> {
220 pub fn new(data: T) -> SliceTypeFind<T> {
221 assert_initialized_main_thread!();
222 SliceTypeFind {
223 probability: None,
224 caps: None,
225 data,
226 }
227 }
228
229 pub fn run(&mut self) {
230 let factories = TypeFindFactory::factories();
231
232 for factory in factories {
233 factory.call_function(self);
234 if let Some(prob) = self.probability {
235 if prob >= TypeFindProbability::Maximum {
236 break;
237 }
238 }
239 }
240 }
241
242 pub fn type_find(data: T) -> (TypeFindProbability, Option<Caps>) {
243 assert_initialized_main_thread!();
244 let mut t = SliceTypeFind {
245 probability: None,
246 caps: None,
247 data,
248 };
249
250 t.run();
251
252 (t.probability.unwrap_or(TypeFindProbability::None), t.caps)
253 }
254}
255
256impl<T: AsRef<[u8]>> TypeFindImpl for SliceTypeFind<T> {
257 fn peek(&mut self, offset: i64, size: u32) -> Option<&[u8]> {
258 let data = self.data.as_ref();
259 let len = data.len();
260
261 let offset = if offset >= 0 {
262 usize::try_from(offset).ok()?
263 } else {
264 let offset = usize::try_from(offset.unsigned_abs()).ok()?;
265 if len < offset {
266 return None;
267 }
268
269 len - offset
270 };
271
272 let size = usize::try_from(size).ok()?;
273 let end_offset = offset.checked_add(size)?;
274 if end_offset <= len {
275 Some(&data[offset..end_offset])
276 } else {
277 None
278 }
279 }
280
281 fn suggest(&mut self, probability: TypeFindProbability, caps: &Caps) {
282 match self.probability {
283 None => {
284 self.probability = Some(probability);
285 self.caps = Some(caps.clone());
286 }
287 Some(old_probability) if old_probability < probability => {
288 self.probability = Some(probability);
289 self.caps = Some(caps.clone());
290 }
291 _ => (),
292 }
293 }
294 fn length(&self) -> Option<u64> {
295 Some(self.data.as_ref().len() as u64)
296 }
297}
298
299#[cfg(test)]
300mod tests {
301 use super::*;
302
303 #[test]
304 fn test_typefind_call_function() {
305 crate::init().unwrap();
306
307 let xml_factory = TypeFindFactory::factories()
308 .into_iter()
309 .find(|f| {
310 f.caps()
311 .map(|c| {
312 c.structure(0)
313 .map(|s| s.name() == "application/xml")
314 .unwrap_or(false)
315 })
316 .unwrap_or(false)
317 })
318 .unwrap();
319
320 let data = b"<?xml version=\"1.0\"?><test>test</test>";
321 let data = &data[..];
322 let mut typefind = SliceTypeFind::new(&data);
323 xml_factory.call_function(&mut typefind);
324
325 assert_eq!(
326 typefind.caps,
327 Some(Caps::builder("application/xml").build())
328 );
329 assert_eq!(typefind.probability, Some(TypeFindProbability::Minimum));
330 }
331
332 #[test]
333 fn test_typefind_register() {
334 crate::init().unwrap();
335
336 TypeFind::register(
337 None,
338 "test_typefind",
339 crate::Rank::PRIMARY,
340 None,
341 Some(&Caps::builder("test/test").build()),
342 |typefind| {
343 assert_eq!(typefind.length(), Some(8));
344 let mut found = false;
345 if let Some(data) = typefind.peek(0, 8) {
346 if data == b"abcdefgh" {
347 found = true;
348 }
349 }
350
351 if found {
352 typefind.suggest(
353 TypeFindProbability::Likely,
354 &Caps::builder("test/test").build(),
355 );
356 }
357 },
358 )
359 .unwrap();
360
361 let data = b"abcdefgh";
362 let data = &data[..];
363 let (probability, caps) = SliceTypeFind::type_find(data);
364
365 assert_eq!(caps, Some(Caps::builder("test/test").build()));
366 assert_eq!(probability, TypeFindProbability::Likely);
367 }
368}