diff --git a/gstreamer/src/enums.rs b/gstreamer/src/enums.rs index 1c8581325..3c40de9fe 100644 --- a/gstreamer/src/enums.rs +++ b/gstreamer/src/enums.rs @@ -12,6 +12,9 @@ use PadLinkReturn; use ClockReturn; use std::error::Error; use std::fmt; +use std::cmp; + +use glib::translate::ToGlib; impl StateChangeReturn { pub fn into_result(self) -> Result { @@ -285,3 +288,29 @@ impl Error for ClockError { } } } + +impl PartialOrd for ::TypeFindProbability { + fn partial_cmp(&self, other: &Self) -> Option { + self.to_glib().partial_cmp(&other.to_glib()) + } +} + +impl Ord for ::TypeFindProbability { + fn cmp(&self, other: &Self) -> cmp::Ordering { + self.to_glib().cmp(&other.to_glib()) + } + +} + +impl PartialOrd for ::Rank { + fn partial_cmp(&self, other: &Self) -> Option { + self.to_glib().partial_cmp(&other.to_glib()) + } +} + +impl Ord for ::Rank { + fn cmp(&self, other: &Self) -> cmp::Ordering { + self.to_glib().cmp(&other.to_glib()) + } + +} diff --git a/gstreamer/src/lib.rs b/gstreamer/src/lib.rs index 164f100ae..a94a8cd0f 100644 --- a/gstreamer/src/lib.rs +++ b/gstreamer/src/lib.rs @@ -126,6 +126,9 @@ pub use clock_time::ClockTime; mod plugin; +mod typefind; +pub use typefind::*; + pub mod format; pub use format::{FormattedValue, GenericFormattedValue, SpecificFormattedValue}; diff --git a/gstreamer/src/typefind.rs b/gstreamer/src/typefind.rs new file mode 100644 index 000000000..fdb41f345 --- /dev/null +++ b/gstreamer/src/typefind.rs @@ -0,0 +1,304 @@ +// Copyright (C) 2017 Sebastian Dröge +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use ffi; +use Plugin; +use TypeFindProbability; +use TypeFindFactory; +use Caps; + +use std::mem; +use std::ptr; +use std::slice; +use std::marker::PhantomData; +use glib_ffi; +use glib::translate::*; + +#[repr(C)] +pub struct TypeFind<'a>(ffi::GstTypeFind, PhantomData<&'a ()>); + +pub trait TypeFindImpl { + fn peek(&mut self, offset: i64, size: u32) -> Option<&[u8]>; + fn suggest(&mut self, probability: TypeFindProbability, caps: &Caps); + fn get_length(&self) -> Option { + None + } +} + +impl<'a> TypeFind<'a> { + pub fn register< + 'b, + 'c, + 'd, + P: Into>, + R: Into>, + S: Into>, + F, + >( + plugin: P, + name: &str, + rank: u32, + extensions: R, + possible_caps: S, + func: F, + ) -> bool + where + F: Fn(&mut TypeFind) + Send + Sync + 'static, + { + let plugin = plugin.into(); + let extensions = extensions.into(); + let possible_caps = possible_caps.into(); + unsafe { + let func: Box> = + Box::new(Box::new(func)); + let func = Box::into_raw(func); + + from_glib(ffi::gst_type_find_register( + plugin.to_glib_none().0, + name.to_glib_none().0, + rank, + Some(type_find_trampoline), + extensions.to_glib_none().0, + possible_caps.to_glib_none().0, + func as *mut _, + Some(type_find_closure_drop), + )) + } + } + + pub fn peek(&mut self, offset: i64, size: u32) -> Option<&[u8]> { + unsafe { + let data = ffi::gst_type_find_peek(&mut self.0, offset, size); + if data.is_null() { + None + } else { + Some(slice::from_raw_parts(data, size as usize)) + } + } + } + + pub fn suggest(&mut self, probability: TypeFindProbability, caps: &Caps) { + unsafe { + ffi::gst_type_find_suggest( + &mut self.0, + probability.to_glib() as u32, + caps.to_glib_none().0, + ); + } + } +} + +unsafe impl<'a> Send for TypeFind<'a> {} + +impl TypeFindFactory { + pub fn call_function(&self, find: &mut TypeFindImpl) { + unsafe { + let find_ptr = &find as *const &mut TypeFindImpl as glib_ffi::gpointer; + let mut find = ffi::GstTypeFind { + peek: Some(type_find_peek), + suggest: Some(type_find_suggest), + data: find_ptr, + get_length: Some(type_find_get_length), + _gst_reserved: [ptr::null_mut(); 4], + }; + + ffi::gst_type_find_factory_call_function(self.to_glib_none().0, &mut find) + } + } +} + +unsafe extern "C" fn type_find_trampoline( + find: *mut ffi::GstTypeFind, + user_data: glib_ffi::gpointer, +) { + callback_guard!(); + let func: &&(Fn(&mut TypeFind) + Send + Sync + 'static) = mem::transmute(user_data); + func(&mut *(find as *mut TypeFind)); +} + +unsafe extern "C" fn type_find_closure_drop(data: glib_ffi::gpointer) { + callback_guard!(); + Box::>::from_raw(data as *mut _); +} + +unsafe extern "C" fn type_find_peek(data: glib_ffi::gpointer, offset: i64, size: u32) -> *const u8 { + callback_guard!(); + let find: &mut &mut TypeFindImpl = &mut *(data as *mut &mut TypeFindImpl); + match find.peek(offset, size) { + None => ptr::null(), + Some(data) => data.as_ptr(), + } +} + +unsafe extern "C" fn type_find_suggest( + data: glib_ffi::gpointer, + probability: u32, + caps: *mut ffi::GstCaps, +) { + callback_guard!(); + let find: &mut &mut TypeFindImpl = &mut *(data as *mut &mut TypeFindImpl); + find.suggest(from_glib(probability as i32), &from_glib_borrow(caps)); +} + +unsafe extern "C" fn type_find_get_length(data: glib_ffi::gpointer) -> u64 { + use std::u64; + + callback_guard!(); + let find: &mut &mut TypeFindImpl = &mut *(data as *mut &mut TypeFindImpl); + find.get_length().unwrap_or(u64::MAX) +} + +pub struct SliceTypeFind> { + pub probability: Option, + pub caps: Option, + data: T, +} + +impl> SliceTypeFind { + pub fn new(data: T) -> SliceTypeFind { + SliceTypeFind { + probability: None, + caps: None, + data: data, + } + } + + pub fn run(&mut self) { + let factories = TypeFindFactory::get_list(); + + for factory in factories { + factory.call_function(self); + if let Some(prob) = self.probability { + if prob >= TypeFindProbability::Maximum { + break; + } + } + } + } + + pub fn type_find(data: T) -> (TypeFindProbability, Option) { + let mut t = SliceTypeFind { + probability: None, + caps: None, + data: data, + }; + + t.run(); + + (t.probability.unwrap_or(TypeFindProbability::None), t.caps) + } +} + +impl> TypeFindImpl for SliceTypeFind { + fn peek(&mut self, offset: i64, size: u32) -> Option<&[u8]> { + let data = self.data.as_ref(); + let len = data.len(); + + let offset = if offset >= 0 { + offset as usize + } else { + if len < offset.abs() as usize { + return None; + } + + len - (offset.abs() as usize) + }; + + let size = size as usize; + if offset + size <= len { + Some(&data[offset..(offset + size)]) + } else { + None + } + } + + fn suggest(&mut self, probability: TypeFindProbability, caps: &Caps) { + match self.probability { + None => { + self.probability = Some(probability); + self.caps = Some(caps.clone()); + } + Some(old_probability) if old_probability < probability => { + self.probability = Some(probability); + self.caps = Some(caps.clone()); + } + _ => (), + } + } + fn get_length(&self) -> Option { + Some(self.data.as_ref().len() as u64) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_typefind_call_function() { + ::init().unwrap(); + + let xml_factory = TypeFindFactory::get_list() + .iter() + .cloned() + .find(|f| { + f.get_caps() + .map(|c| { + c.get_structure(0).unwrap().get_name() == "application/xml" + }) + .unwrap_or(false) + }) + .unwrap(); + + let data = b"test"; + let data = &data[..]; + let mut typefind = SliceTypeFind::new(&data); + xml_factory.call_function(&mut typefind); + + assert_eq!( + typefind.caps, + Some(Caps::new_simple("application/xml", &[])) + ); + assert_eq!(typefind.probability, Some(TypeFindProbability::Minimum)); + } + + #[test] + fn test_typefind_register() { + ::init().unwrap(); + + TypeFind::register( + None, + "test_typefind", + ::Rank::Primary.to_glib() as u32, + None, + &Caps::new_simple("test/test", &[]), + |typefind| { + let mut found = false; + if let Some(data) = typefind.peek(0, 8) { + if data == b"abcdefgh" { + found = true; + } + } + + if found { + typefind.suggest( + TypeFindProbability::Likely, + &Caps::new_simple("test/test", &[]), + ); + } + }, + ); + + let data = b"abcdefgh"; + let data = &data[..]; + let (probability, caps) = SliceTypeFind::type_find(&data); + + assert_eq!(caps, Some(Caps::new_simple("test/test", &[]))); + assert_eq!(probability, TypeFindProbability::Likely); + } +}