// Copyright (C) 2018 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 std::borrow::{Borrow, BorrowMut, ToOwned}; use std::ffi::CStr; use std::fmt; use std::marker::PhantomData; use std::mem; use std::ops::{Deref, DerefMut}; use std::ptr; use std::str; use once_cell::sync::Lazy; use glib; use glib::translate::{ from_glib, from_glib_full, FromGlibPtrFull, FromGlibPtrNone, GlibPtrDefault, Stash, StashMut, ToGlibPtr, ToGlibPtrMut, }; use glib_sys::gpointer; use gobject_sys; use gst_sys; pub struct CapsFeatures(ptr::NonNull, PhantomData); unsafe impl Send for CapsFeatures {} unsafe impl Sync for CapsFeatures {} impl CapsFeatures { pub fn new(features: &[&str]) -> Self { assert_initialized_main_thread!(); let mut f = Self::new_empty(); for feature in features { f.add(feature); } f } pub fn new_empty() -> Self { assert_initialized_main_thread!(); unsafe { CapsFeatures( ptr::NonNull::new_unchecked( gst_sys::gst_caps_features_new_empty() as *mut CapsFeaturesRef ), PhantomData, ) } } pub fn new_any() -> Self { assert_initialized_main_thread!(); unsafe { CapsFeatures( ptr::NonNull::new_unchecked( gst_sys::gst_caps_features_new_any() as *mut CapsFeaturesRef ), PhantomData, ) } } pub unsafe fn into_ptr(self) -> *mut gst_sys::GstCapsFeatures { let s = mem::ManuallyDrop::new(self); s.0.as_ptr() as *mut CapsFeaturesRef as *mut gst_sys::GstCapsFeatures } } impl Deref for CapsFeatures { type Target = CapsFeaturesRef; fn deref(&self) -> &CapsFeaturesRef { unsafe { self.0.as_ref() } } } impl DerefMut for CapsFeatures { fn deref_mut(&mut self) -> &mut CapsFeaturesRef { unsafe { self.0.as_mut() } } } impl AsRef for CapsFeatures { fn as_ref(&self) -> &CapsFeaturesRef { self.deref() } } impl AsMut for CapsFeatures { fn as_mut(&mut self) -> &mut CapsFeaturesRef { self.deref_mut() } } impl Clone for CapsFeatures { fn clone(&self) -> Self { unsafe { let ptr = gst_sys::gst_caps_features_copy(&self.0.as_ref().0) as *mut CapsFeaturesRef; assert!(!ptr.is_null()); CapsFeatures(ptr::NonNull::new_unchecked(ptr), PhantomData) } } } impl Drop for CapsFeatures { fn drop(&mut self) { unsafe { gst_sys::gst_caps_features_free(&mut self.0.as_mut().0) } } } impl fmt::Debug for CapsFeatures { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_tuple("CapsFeatures") .field(&self.to_string()) .finish() } } impl fmt::Display for CapsFeatures { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { // Need to make sure to not call ToString::to_string() here, which // we have because of the Display impl. We need CapsFeaturesRef::to_string() f.write_str(&CapsFeaturesRef::to_string(self.as_ref())) } } impl str::FromStr for CapsFeatures { type Err = glib::BoolError; fn from_str(s: &str) -> Result { assert_initialized_main_thread!(); unsafe { let ptr = gst_sys::gst_caps_features_from_string(s.to_glib_none().0); if ptr.is_null() { return Err(glib_bool_error!( "Failed to parse caps features from string" )); } Ok(CapsFeatures( ptr::NonNull::new_unchecked(ptr as *mut CapsFeaturesRef), PhantomData, )) } } } impl Borrow for CapsFeatures { fn borrow(&self) -> &CapsFeaturesRef { unsafe { self.0.as_ref() } } } impl BorrowMut for CapsFeatures { fn borrow_mut(&mut self) -> &mut CapsFeaturesRef { unsafe { self.0.as_mut() } } } impl glib::types::StaticType for CapsFeatures { fn static_type() -> glib::types::Type { unsafe { from_glib(gst_sys::gst_caps_features_get_type()) } } } impl<'a> ToGlibPtr<'a, *const gst_sys::GstCapsFeatures> for CapsFeatures { type Storage = &'a Self; fn to_glib_none(&'a self) -> Stash<'a, *const gst_sys::GstCapsFeatures, Self> { unsafe { Stash(&self.0.as_ref().0, self) } } fn to_glib_full(&self) -> *const gst_sys::GstCapsFeatures { unsafe { gst_sys::gst_caps_features_copy(&self.0.as_ref().0) } } } impl<'a> ToGlibPtr<'a, *mut gst_sys::GstCapsFeatures> for CapsFeatures { type Storage = &'a Self; fn to_glib_none(&'a self) -> Stash<'a, *mut gst_sys::GstCapsFeatures, Self> { unsafe { Stash(&self.0.as_ref().0 as *const _ as *mut _, self) } } fn to_glib_full(&self) -> *mut gst_sys::GstCapsFeatures { unsafe { gst_sys::gst_caps_features_copy(&self.0.as_ref().0) } } } impl<'a> ToGlibPtrMut<'a, *mut gst_sys::GstCapsFeatures> for CapsFeatures { type Storage = &'a mut Self; fn to_glib_none_mut(&'a mut self) -> StashMut<*mut gst_sys::GstCapsFeatures, Self> { unsafe { StashMut(&mut self.0.as_mut().0, self) } } } impl FromGlibPtrNone<*const gst_sys::GstCapsFeatures> for CapsFeatures { unsafe fn from_glib_none(ptr: *const gst_sys::GstCapsFeatures) -> Self { assert!(!ptr.is_null()); let ptr = gst_sys::gst_caps_features_copy(ptr); assert!(!ptr.is_null()); CapsFeatures( ptr::NonNull::new_unchecked(ptr as *mut CapsFeaturesRef), PhantomData, ) } } impl FromGlibPtrNone<*mut gst_sys::GstCapsFeatures> for CapsFeatures { unsafe fn from_glib_none(ptr: *mut gst_sys::GstCapsFeatures) -> Self { assert!(!ptr.is_null()); let ptr = gst_sys::gst_caps_features_copy(ptr); assert!(!ptr.is_null()); CapsFeatures( ptr::NonNull::new_unchecked(ptr as *mut CapsFeaturesRef), PhantomData, ) } } impl FromGlibPtrFull<*const gst_sys::GstCapsFeatures> for CapsFeatures { unsafe fn from_glib_full(ptr: *const gst_sys::GstCapsFeatures) -> Self { assert!(!ptr.is_null()); CapsFeatures( ptr::NonNull::new_unchecked(ptr as *mut CapsFeaturesRef), PhantomData, ) } } impl FromGlibPtrFull<*mut gst_sys::GstCapsFeatures> for CapsFeatures { unsafe fn from_glib_full(ptr: *mut gst_sys::GstCapsFeatures) -> Self { assert!(!ptr.is_null()); CapsFeatures( ptr::NonNull::new_unchecked(ptr as *mut CapsFeaturesRef), PhantomData, ) } } impl<'a> glib::value::FromValueOptional<'a> for CapsFeatures { unsafe fn from_value_optional(v: &'a glib::Value) -> Option { <&'a CapsFeaturesRef as glib::value::FromValueOptional<'a>>::from_value_optional(v) .map(ToOwned::to_owned) } } impl glib::value::SetValue for CapsFeatures { unsafe fn set_value(v: &mut glib::Value, s: &Self) { ::set_value(v, s.as_ref()) } } impl glib::value::SetValueOptional for CapsFeatures { unsafe fn set_value_optional(v: &mut glib::Value, s: Option<&Self>) { ::set_value_optional( v, s.map(|s| s.as_ref()), ) } } impl GlibPtrDefault for CapsFeatures { type GlibType = *mut gst_sys::GstCapsFeatures; } #[repr(C)] pub struct CapsFeaturesRef(gst_sys::GstCapsFeatures); impl CapsFeaturesRef { pub unsafe fn from_glib_borrow<'a>( ptr: *const gst_sys::GstCapsFeatures, ) -> &'a CapsFeaturesRef { assert!(!ptr.is_null()); &*(ptr as *mut CapsFeaturesRef) } pub unsafe fn from_glib_borrow_mut<'a>( ptr: *mut gst_sys::GstCapsFeatures, ) -> &'a mut CapsFeaturesRef { assert!(!ptr.is_null()); &mut *(ptr as *mut CapsFeaturesRef) } pub unsafe fn as_ptr(&self) -> *const gst_sys::GstCapsFeatures { self as *const Self as *const gst_sys::GstCapsFeatures } pub unsafe fn as_mut_ptr(&self) -> *mut gst_sys::GstCapsFeatures { self as *const Self as *mut gst_sys::GstCapsFeatures } pub fn is_empty(&self) -> bool { self.get_size() == 0 && !self.is_any() } pub fn is_any(&self) -> bool { unsafe { from_glib(gst_sys::gst_caps_features_is_any(self.as_ptr())) } } pub fn contains(&self, feature: &str) -> bool { unsafe { from_glib(gst_sys::gst_caps_features_contains( self.as_ptr(), feature.to_glib_none().0, )) } } pub fn get_size(&self) -> u32 { unsafe { gst_sys::gst_caps_features_get_size(self.as_ptr()) } } pub fn get_nth(&self, idx: u32) -> Option<&str> { if idx >= self.get_size() { return None; } unsafe { let feature = gst_sys::gst_caps_features_get_nth(self.as_ptr(), idx); if feature.is_null() { return None; } Some(CStr::from_ptr(feature).to_str().unwrap()) } } pub fn add(&mut self, feature: &str) { unsafe { gst_sys::gst_caps_features_add(self.as_mut_ptr(), feature.to_glib_none().0) } } pub fn remove(&mut self, feature: &str) { unsafe { gst_sys::gst_caps_features_remove(self.as_mut_ptr(), feature.to_glib_none().0) } } pub fn iter(&self) -> Iter { Iter::new(self) } // This is not an equivalence relation with regards to ANY. Everything is equal to ANY pub fn is_equal(&self, other: &CapsFeaturesRef) -> bool { unsafe { from_glib(gst_sys::gst_caps_features_is_equal( self.as_ptr(), other.as_ptr(), )) } } } impl glib::types::StaticType for CapsFeaturesRef { fn static_type() -> glib::types::Type { unsafe { from_glib(gst_sys::gst_structure_get_type()) } } } impl<'a> glib::value::FromValueOptional<'a> for &'a CapsFeaturesRef { unsafe fn from_value_optional(v: &'a glib::Value) -> Option { let ptr = gobject_sys::g_value_get_boxed(v.to_glib_none().0); if ptr.is_null() { None } else { Some(CapsFeaturesRef::from_glib_borrow( ptr as *const gst_sys::GstCapsFeatures, )) } } } impl glib::value::SetValue for CapsFeaturesRef { unsafe fn set_value(v: &mut glib::Value, s: &Self) { gobject_sys::g_value_set_boxed(v.to_glib_none_mut().0, s.as_ptr() as gpointer); } } impl glib::value::SetValueOptional for CapsFeaturesRef { unsafe fn set_value_optional(v: &mut glib::Value, s: Option<&Self>) { if let Some(s) = s { gobject_sys::g_value_set_boxed(v.to_glib_none_mut().0, s.as_ptr() as gpointer); } else { gobject_sys::g_value_set_boxed(v.to_glib_none_mut().0, ptr::null_mut()); } } } #[derive(Debug)] pub struct Iter<'a> { caps_features: &'a CapsFeaturesRef, idx: u32, n_features: u32, } impl<'a> Iter<'a> { fn new(caps_features: &'a CapsFeaturesRef) -> Iter<'a> { skip_assert_initialized!(); let n_features = caps_features.get_size(); Iter { caps_features, idx: 0, n_features, } } } impl<'a> Iterator for Iter<'a> { type Item = &'a str; fn next(&mut self) -> Option { if self.idx >= self.n_features { return None; } unsafe { let feature = gst_sys::gst_caps_features_get_nth(self.caps_features.as_ptr(), self.idx); if feature.is_null() { return None; } self.idx += 1; Some(CStr::from_ptr(feature).to_str().unwrap()) } } fn size_hint(&self) -> (usize, Option) { if self.idx == self.n_features { return (0, Some(0)); } let remaining = (self.n_features - self.idx) as usize; (remaining, Some(remaining)) } } impl<'a> DoubleEndedIterator for Iter<'a> { fn next_back(&mut self) -> Option { if self.idx == self.n_features { return None; } self.n_features -= 1; unsafe { let feature = gst_sys::gst_caps_features_get_nth(self.caps_features.as_ptr(), self.n_features); if feature.is_null() { return None; } Some(CStr::from_ptr(feature).to_str().unwrap()) } } } impl<'a> ExactSizeIterator for Iter<'a> {} impl fmt::Debug for CapsFeaturesRef { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_tuple("CapsFeatures") .field(&self.to_string()) .finish() } } impl fmt::Display for CapsFeaturesRef { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let s = unsafe { glib::GString::from_glib_full(gst_sys::gst_caps_features_to_string(self.as_ptr())) }; f.write_str(&s) } } impl ToOwned for CapsFeaturesRef { type Owned = CapsFeatures; fn to_owned(&self) -> CapsFeatures { unsafe { from_glib_full(gst_sys::gst_caps_features_copy(self.as_ptr() as *const _) as *mut _) } } } unsafe impl Sync for CapsFeaturesRef {} unsafe impl Send for CapsFeaturesRef {} pub static CAPS_FEATURE_MEMORY_SYSTEM_MEMORY: Lazy<&'static str> = Lazy::new(|| unsafe { CStr::from_ptr(gst_sys::GST_CAPS_FEATURE_MEMORY_SYSTEM_MEMORY) .to_str() .unwrap() }); pub static CAPS_FEATURES_MEMORY_SYSTEM_MEMORY: Lazy = Lazy::new(|| CapsFeatures::new(&[*CAPS_FEATURE_MEMORY_SYSTEM_MEMORY])); #[cfg(test)] mod tests { use super::*; #[test] fn test_from_value_optional() { ::init().unwrap(); let a = glib::value::Value::from(None::<&CapsFeatures>); assert!(a.get::().unwrap().is_none()); let b = glib::value::Value::from(&CapsFeatures::new_empty()); assert!(b.get::().unwrap().is_some()); } }