gstreamer-rs/gstreamer/src/typefind.rs
Sebastian Dröge 2ba5105b80 Implement Sync/Send for more types and don't implement Send for TypeFind
They can actually be shared with multiple threads at the same time
safely as all functions requiring an immutable reference are
thread-safe.

OTOH TypeFind can't be shared safely between different threads as not
all implementations of the TypeFind struct are thread-safe.
2019-12-18 18:37:44 +02:00

308 lines
8.6 KiB
Rust

// Copyright (C) 2017 Sebastian Dröge <sebastian@centricular.com>
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use gst_sys;
use Caps;
use Plugin;
use Rank;
use TypeFindFactory;
use TypeFindProbability;
use glib;
use glib::translate::*;
use glib_sys;
use std::marker::PhantomData;
use std::ptr;
use std::slice;
#[repr(C)]
#[derive(Debug)]
pub struct TypeFind<'a>(gst_sys::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<u64> {
None
}
}
impl<'a> TypeFind<'a> {
pub fn register<F>(
plugin: Option<&Plugin>,
name: &str,
rank: Rank,
extensions: Option<&str>,
possible_caps: Option<&Caps>,
func: F,
) -> Result<(), glib::error::BoolError>
where
F: Fn(&mut TypeFind) + Send + Sync + 'static,
{
unsafe {
let func: Box<F> = Box::new(func);
let func = Box::into_raw(func);
let res = gst_sys::gst_type_find_register(
plugin.to_glib_none().0,
name.to_glib_none().0,
rank.to_glib() as u32,
Some(type_find_trampoline::<F>),
extensions.to_glib_none().0,
possible_caps.to_glib_none().0,
func as *mut _,
Some(type_find_closure_drop::<F>),
);
glib_result_from_gboolean!(res, "Failed to register typefind factory")
}
}
pub fn peek(&mut self, offset: i64, size: u32) -> Option<&[u8]> {
unsafe {
let data = gst_sys::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 {
gst_sys::gst_type_find_suggest(
&mut self.0,
probability.to_glib() as u32,
caps.to_glib_none().0,
);
}
}
pub fn get_length(&mut self) -> Option<u64> {
unsafe {
let len = gst_sys::gst_type_find_get_length(&mut self.0);
if len == 0 {
None
} else {
Some(len)
}
}
}
}
impl TypeFindFactory {
pub fn call_function(&self, find: &mut dyn TypeFindImpl) {
unsafe {
let find_ptr = &find as *const &mut dyn TypeFindImpl as glib_sys::gpointer;
let mut find = gst_sys::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],
};
gst_sys::gst_type_find_factory_call_function(self.to_glib_none().0, &mut find)
}
}
}
unsafe extern "C" fn type_find_trampoline<F: Fn(&mut TypeFind) + Send + Sync + 'static>(
find: *mut gst_sys::GstTypeFind,
user_data: glib_sys::gpointer,
) {
let func: &F = &*(user_data as *const F);
func(&mut *(find as *mut TypeFind));
}
unsafe extern "C" fn type_find_closure_drop<F: Fn(&mut TypeFind) + Send + Sync + 'static>(
data: glib_sys::gpointer,
) {
Box::<F>::from_raw(data as *mut _);
}
unsafe extern "C" fn type_find_peek(data: glib_sys::gpointer, offset: i64, size: u32) -> *const u8 {
let find: &mut &mut dyn TypeFindImpl = &mut *(data as *mut &mut dyn TypeFindImpl);
match find.peek(offset, size) {
None => ptr::null(),
Some(data) => data.as_ptr(),
}
}
unsafe extern "C" fn type_find_suggest(
data: glib_sys::gpointer,
probability: u32,
caps: *mut gst_sys::GstCaps,
) {
let find: &mut &mut dyn TypeFindImpl = &mut *(data as *mut &mut dyn TypeFindImpl);
find.suggest(from_glib(probability as i32), &from_glib_borrow(caps));
}
unsafe extern "C" fn type_find_get_length(data: glib_sys::gpointer) -> u64 {
use std::u64;
let find: &mut &mut dyn TypeFindImpl = &mut *(data as *mut &mut dyn TypeFindImpl);
find.get_length().unwrap_or(u64::MAX)
}
#[derive(Debug)]
pub struct SliceTypeFind<T: AsRef<[u8]>> {
pub probability: Option<TypeFindProbability>,
pub caps: Option<Caps>,
data: T,
}
impl<T: AsRef<[u8]>> SliceTypeFind<T> {
pub fn new(data: T) -> SliceTypeFind<T> {
SliceTypeFind {
probability: None,
caps: None,
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<Caps>) {
let mut t = SliceTypeFind {
probability: None,
caps: None,
data,
};
t.run();
(t.probability.unwrap_or(TypeFindProbability::None), t.caps)
}
}
impl<T: AsRef<[u8]>> TypeFindImpl for SliceTypeFind<T> {
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<u64> {
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)
.map(|s| s.get_name() == "application/xml")
.unwrap_or(false)
})
.unwrap_or(false)
})
.unwrap();
let data = b"<?xml version=\"1.0\"?><test>test</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,
None,
Some(&Caps::new_simple("test/test", &[])),
|typefind| {
assert_eq!(typefind.get_length(), Some(8));
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", &[]),
);
}
},
)
.unwrap();
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);
}
}