From 564f9faf84b2426a199b0bba92d31eb1106ce2ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Fri, 28 Sep 2018 12:00:08 +0300 Subject: [PATCH] Add support for CapsFeatures Fixes https://github.com/sdroege/gstreamer-rs/issues/13 --- gstreamer/src/caps.rs | 269 ++++++++++++--- gstreamer/src/caps_features.rs | 472 +++++++++++++++++++++++++++ gstreamer/src/caps_features_serde.rs | 166 ++++++++++ gstreamer/src/caps_serde.rs | 211 ++++++++++-- gstreamer/src/lib.rs | 4 + 5 files changed, 1049 insertions(+), 73 deletions(-) create mode 100644 gstreamer/src/caps_features.rs create mode 100644 gstreamer/src/caps_features_serde.rs diff --git a/gstreamer/src/caps.rs b/gstreamer/src/caps.rs index a6e2ed860..09353258d 100644 --- a/gstreamer/src/caps.rs +++ b/gstreamer/src/caps.rs @@ -6,8 +6,10 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use caps_features::*; use miniobject::*; use std::fmt; +use std::ptr; use std::str; use structure::*; @@ -68,12 +70,27 @@ impl GstRc { unsafe { from_glib_full(ffi::gst_caps_merge(caps.into_ptr(), other.into_ptr())) } } - pub fn merge_structure(caps: Self, other: Structure) -> Self { + pub fn merge_structure(caps: Self, structure: Structure) -> Self { skip_assert_initialized!(); unsafe { from_glib_full(ffi::gst_caps_merge_structure( caps.into_ptr(), - other.into_ptr(), + structure.into_ptr(), + )) + } + } + + pub fn merge_structure_full( + caps: Self, + structure: Structure, + features: Option, + ) -> Self { + skip_assert_initialized!(); + unsafe { + from_glib_full(ffi::gst_caps_merge_structure_full( + caps.into_ptr(), + structure.into_ptr(), + features.map(|f| f.into_ptr()).unwrap_or(ptr::null_mut()), )) } } @@ -133,9 +150,7 @@ impl CapsRef { return None; } - Some(StructureRef::from_glib_borrow( - structure as *const ffi::GstStructure, - )) + Some(StructureRef::from_glib_borrow(structure)) } } @@ -150,9 +165,41 @@ impl CapsRef { return None; } - Some(StructureRef::from_glib_borrow_mut( - structure as *mut ffi::GstStructure, - )) + Some(StructureRef::from_glib_borrow_mut(structure)) + } + } + + pub fn get_features(&self, idx: u32) -> Option<&CapsFeaturesRef> { + if idx >= self.get_size() { + return None; + } + + unsafe { + let features = ffi::gst_caps_get_features(self.as_ptr(), idx); + Some(CapsFeaturesRef::from_glib_borrow(features)) + } + } + + pub fn get_mut_features(&mut self, idx: u32) -> Option<&mut CapsFeaturesRef> { + if idx >= self.get_size() { + return None; + } + + unsafe { + let features = ffi::gst_caps_get_features(self.as_ptr(), idx); + Some(CapsFeaturesRef::from_glib_borrow_mut(features)) + } + } + + pub fn set_features(&mut self, idx: u32, features: Option) { + assert!(idx < self.get_size()); + + unsafe { + ffi::gst_caps_set_features( + self.as_mut_ptr(), + idx, + features.map(|f| f.into_ptr()).unwrap_or(ptr::null_mut()), + ) } } @@ -168,10 +215,28 @@ impl CapsRef { IterMut::new(self) } + pub fn iter_with_features(&self) -> IterFeatures { + IterFeatures::new(self) + } + + pub fn iter_with_features_mut(&mut self) -> IterFeaturesMut { + IterFeaturesMut::new(self) + } + pub fn append_structure(&mut self, structure: Structure) { unsafe { ffi::gst_caps_append_structure(self.as_mut_ptr(), structure.into_ptr()) } } + pub fn append_structure_full(&mut self, structure: Structure, features: Option) { + unsafe { + ffi::gst_caps_append_structure_full( + self.as_mut_ptr(), + structure.into_ptr(), + features.map(|f| f.into_ptr()).unwrap_or(ptr::null_mut()), + ) + } + } + pub fn remove_structure(&mut self, idx: u32) { unsafe { ffi::gst_caps_remove_structure(self.as_mut_ptr(), idx) } } @@ -250,6 +315,20 @@ impl CapsRef { } } + pub fn is_subset_structure_full( + &self, + structure: &StructureRef, + features: Option<&CapsFeaturesRef>, + ) -> bool { + unsafe { + from_glib(ffi::gst_caps_is_subset_structure_full( + self.as_ptr(), + structure.as_ptr(), + features.map(|f| f.as_ptr()).unwrap_or(ptr::null()), + )) + } + } + pub fn subtract(&self, other: &Self) -> Caps { skip_assert_initialized!(); unsafe { @@ -268,7 +347,7 @@ impl glib::types::StaticType for CapsRef { } macro_rules! define_iter( - ($name:ident, $typ:ty, $styp:ty) => { + ($name:ident, $typ:ty, $styp:ty, $get_item:expr) => { pub struct $name<'a> { caps: $typ, idx: u32, @@ -297,15 +376,13 @@ macro_rules! define_iter( } unsafe { - let structure = ffi::gst_caps_get_structure(self.caps.as_ptr(), self.idx); - if structure.is_null() { + let item = $get_item(self.caps, self.idx); + if item.is_none() { return None; } self.idx += 1; - Some(StructureRef::from_glib_borrow_mut( - structure as *mut ffi::GstStructure, - )) + item } } @@ -329,14 +406,7 @@ macro_rules! define_iter( self.n_structures -= 1; unsafe { - let structure = ffi::gst_caps_get_structure(self.caps.as_ptr(), self.n_structures); - if structure.is_null() { - return None; - } - - Some(StructureRef::from_glib_borrow_mut( - structure as *mut ffi::GstStructure, - )) + $get_item(self.caps, self.n_structures) } } } @@ -345,8 +415,70 @@ macro_rules! define_iter( } ); -define_iter!(Iter, &'a CapsRef, &'a StructureRef); -define_iter!(IterMut, &'a mut CapsRef, &'a mut StructureRef); +define_iter!( + Iter, + &'a CapsRef, + &'a StructureRef, + |caps: &CapsRef, idx| { + let ptr = ffi::gst_caps_get_structure(caps.as_ptr(), idx); + if ptr.is_null() { + None + } else { + Some(StructureRef::from_glib_borrow( + ptr as *const ffi::GstStructure, + )) + } + } +); +define_iter!( + IterMut, + &'a mut CapsRef, + &'a mut StructureRef, + |caps: &CapsRef, idx| { + let ptr = ffi::gst_caps_get_structure(caps.as_ptr(), idx); + if ptr.is_null() { + None + } else { + Some(StructureRef::from_glib_borrow_mut( + ptr as *mut ffi::GstStructure, + )) + } + } +); +define_iter!( + IterFeatures, + &'a CapsRef, + (&'a StructureRef, &'a CapsFeaturesRef), + |caps: &CapsRef, idx| { + let ptr1 = ffi::gst_caps_get_structure(caps.as_ptr(), idx); + let ptr2 = ffi::gst_caps_get_features(caps.as_ptr(), idx); + if ptr1.is_null() || ptr2.is_null() { + None + } else { + Some(( + StructureRef::from_glib_borrow(ptr1 as *const ffi::GstStructure), + CapsFeaturesRef::from_glib_borrow(ptr2 as *const ffi::GstCapsFeatures), + )) + } + } +); +define_iter!( + IterFeaturesMut, + &'a mut CapsRef, + (&'a mut StructureRef, &'a mut CapsFeaturesRef), + |caps: &CapsRef, idx| { + let ptr1 = ffi::gst_caps_get_structure(caps.as_ptr(), idx); + let ptr2 = ffi::gst_caps_get_features(caps.as_ptr(), idx); + if ptr1.is_null() || ptr2.is_null() { + None + } else { + Some(( + StructureRef::from_glib_borrow_mut(ptr1 as *mut ffi::GstStructure), + CapsFeaturesRef::from_glib_borrow_mut(ptr2 as *mut ffi::GstCapsFeatures), + )) + } + } +); impl fmt::Debug for CapsRef { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { @@ -379,25 +511,47 @@ impl ToOwned for CapsRef { unsafe impl Sync for CapsRef {} unsafe impl Send for CapsRef {} -pub struct Builder { +pub struct Builder<'a> { s: ::Structure, + features: Option<&'a [&'a str]>, + any_features: bool, } -impl Builder { - fn new(name: &str) -> Self { +impl<'a> Builder<'a> { + fn new<'b>(name: &'b str) -> Builder<'a> { Builder { s: ::Structure::new_empty(name), + features: None, + any_features: false, } } - pub fn field(mut self, name: &str, value: &V) -> Self { + pub fn field<'b, V: ToSendValue>(mut self, name: &'b str, value: &'b V) -> Self { self.s.set(name, value); self } + pub fn features(mut self, features: &'a [&'a str]) -> Self { + self.features = Some(features); + self + } + + pub fn any_features(mut self) -> Self { + self.any_features = true; + self + } + pub fn build(self) -> Caps { let mut caps = Caps::new_empty(); - caps.get_mut().unwrap().append_structure(self.s); + let features = if self.any_features { + Some(CapsFeatures::new_any()) + } else { + self.features.map(|f| CapsFeatures::new(f)) + }; + + caps.get_mut() + .unwrap() + .append_structure_full(self.s, features); caps } } @@ -412,7 +566,7 @@ mod tests { fn test_simple() { ::init().unwrap(); - let caps = Caps::new_simple( + let mut caps = Caps::new_simple( "foo/bar", &[ ("int", &12), @@ -427,19 +581,36 @@ mod tests { "foo/bar, int=(int)12, bool=(boolean)true, string=(string)bla, fraction=(fraction)1/2, array=(int)< 1, 2 >" ); - let s = caps.get_structure(0).unwrap(); - assert_eq!( - s, - Structure::new( - "foo/bar", - &[ - ("int", &12), - ("bool", &true), - ("string", &"bla"), - ("fraction", &Fraction::new(1, 2)), - ("array", &Array::new(&[&1, &2])), - ], - ).as_ref() + { + let s = caps.get_structure(0).unwrap(); + assert_eq!( + s, + Structure::new( + "foo/bar", + &[ + ("int", &12), + ("bool", &true), + ("string", &"bla"), + ("fraction", &Fraction::new(1, 2)), + ("array", &Array::new(&[&1, &2])), + ], + ).as_ref() + ); + } + assert!( + caps.get_features(0) + .unwrap() + .is_equal(::CAPS_FEATURES_MEMORY_SYSTEM_MEMORY.as_ref()) + ); + + { + let caps = caps.get_mut().unwrap(); + caps.set_features(0, Some(CapsFeatures::new(&["foo:bla"]))); + } + assert!( + caps.get_features(0) + .unwrap() + .is_equal(CapsFeatures::new(&["foo:bla"]).as_ref()) ); } @@ -458,5 +629,17 @@ mod tests { caps.to_string(), "foo/bar, int=(int)12, bool=(boolean)true, string=(string)bla, fraction=(fraction)1/2, array=(int)< 1, 2 >" ); + + let caps = Caps::builder("foo/bar") + .field("int", &12) + .any_features() + .build(); + assert_eq!(caps.to_string(), "foo/bar(ANY), int=(int)12"); + + let caps = Caps::builder("foo/bar") + .field("int", &12) + .features(&["foo:bla", "foo:baz"]) + .build(); + assert_eq!(caps.to_string(), "foo/bar(foo:bla, foo:baz), int=(int)12"); } } diff --git a/gstreamer/src/caps_features.rs b/gstreamer/src/caps_features.rs new file mode 100644 index 000000000..07931679d --- /dev/null +++ b/gstreamer/src/caps_features.rs @@ -0,0 +1,472 @@ +// 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 ffi; +use glib; +use glib::translate::{ + from_glib, from_glib_full, from_glib_none, FromGlibPtrFull, FromGlibPtrNone, GlibPtrDefault, + Stash, StashMut, ToGlibPtr, ToGlibPtrMut, +}; +use glib_ffi::gpointer; +use gobject_ffi; + +pub struct CapsFeatures(ptr::NonNull, PhantomData); +unsafe impl Send for CapsFeatures {} +unsafe impl Sync for CapsFeatures {} + +impl CapsFeatures { + pub fn new(features: &[&str]) -> Self { + 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( + ffi::gst_caps_features_new_empty() as *mut CapsFeaturesRef + ), + PhantomData, + ) + } + } + + pub fn new_any() -> Self { + assert_initialized_main_thread!(); + unsafe { + CapsFeatures( + ptr::NonNull::new_unchecked( + ffi::gst_caps_features_new_any() as *mut CapsFeaturesRef + ), + PhantomData, + ) + } + } + + pub fn from_string(value: &str) -> Option { + assert_initialized_main_thread!(); + unsafe { + let ptr = ffi::gst_caps_features_from_string(value.to_glib_none().0); + if ptr.is_null() { + return None; + } + + Some(CapsFeatures( + ptr::NonNull::new_unchecked(ptr as *mut CapsFeaturesRef), + PhantomData, + )) + } + } + + pub unsafe fn into_ptr(self) -> *mut ffi::GstCapsFeatures { + let ptr = self.0.as_ptr() as *mut CapsFeaturesRef as *mut ffi::GstCapsFeatures; + mem::forget(self); + + ptr + } +} + +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 = ffi::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 { ffi::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 = (); + + fn from_str(s: &str) -> Result { + skip_assert_initialized!(); + CapsFeatures::from_string(s).ok_or(()) + } +} + +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(ffi::gst_caps_features_get_type()) } + } +} + +impl<'a> ToGlibPtr<'a, *const ffi::GstCapsFeatures> for CapsFeatures { + type Storage = &'a Self; + + fn to_glib_none(&'a self) -> Stash<'a, *const ffi::GstCapsFeatures, Self> { + unsafe { Stash(&self.0.as_ref().0, self) } + } + + fn to_glib_full(&self) -> *const ffi::GstCapsFeatures { + unsafe { ffi::gst_caps_features_copy(&self.0.as_ref().0) } + } +} + +impl<'a> ToGlibPtr<'a, *mut ffi::GstCapsFeatures> for CapsFeatures { + type Storage = &'a Self; + + fn to_glib_none(&'a self) -> Stash<'a, *mut ffi::GstCapsFeatures, Self> { + unsafe { Stash(&self.0.as_ref().0 as *const _ as *mut _, self) } + } + + fn to_glib_full(&self) -> *mut ffi::GstCapsFeatures { + unsafe { ffi::gst_caps_features_copy(&self.0.as_ref().0) } + } +} + +impl<'a> ToGlibPtrMut<'a, *mut ffi::GstCapsFeatures> for CapsFeatures { + type Storage = &'a mut Self; + + fn to_glib_none_mut(&'a mut self) -> StashMut<*mut ffi::GstCapsFeatures, Self> { + unsafe { StashMut(&mut self.0.as_mut().0, self) } + } +} + +impl FromGlibPtrNone<*const ffi::GstCapsFeatures> for CapsFeatures { + unsafe fn from_glib_none(ptr: *const ffi::GstCapsFeatures) -> Self { + assert!(!ptr.is_null()); + let ptr = ffi::gst_caps_features_copy(ptr); + assert!(!ptr.is_null()); + CapsFeatures( + ptr::NonNull::new_unchecked(ptr as *mut CapsFeaturesRef), + PhantomData, + ) + } +} + +impl FromGlibPtrNone<*mut ffi::GstCapsFeatures> for CapsFeatures { + unsafe fn from_glib_none(ptr: *mut ffi::GstCapsFeatures) -> Self { + assert!(!ptr.is_null()); + let ptr = ffi::gst_caps_features_copy(ptr); + assert!(!ptr.is_null()); + CapsFeatures( + ptr::NonNull::new_unchecked(ptr as *mut CapsFeaturesRef), + PhantomData, + ) + } +} + +impl FromGlibPtrFull<*const ffi::GstCapsFeatures> for CapsFeatures { + unsafe fn from_glib_full(ptr: *const ffi::GstCapsFeatures) -> Self { + assert!(!ptr.is_null()); + CapsFeatures( + ptr::NonNull::new_unchecked(ptr as *mut CapsFeaturesRef), + PhantomData, + ) + } +} + +impl FromGlibPtrFull<*mut ffi::GstCapsFeatures> for CapsFeatures { + unsafe fn from_glib_full(ptr: *mut ffi::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 { + let ptr = gobject_ffi::g_value_get_boxed(v.to_glib_none().0); + assert!(!ptr.is_null()); + from_glib_none(ptr as *const ffi::GstCapsFeatures) + } +} + +impl glib::value::SetValue for CapsFeatures { + unsafe fn set_value(v: &mut glib::Value, s: &Self) { + gobject_ffi::g_value_set_boxed(v.to_glib_none_mut().0, s.0.as_ptr() as gpointer); + } +} + +impl glib::value::SetValueOptional for CapsFeatures { + unsafe fn set_value_optional(v: &mut glib::Value, s: Option<&Self>) { + if let Some(s) = s { + gobject_ffi::g_value_set_boxed(v.to_glib_none_mut().0, s.as_ptr() as gpointer); + } else { + gobject_ffi::g_value_set_boxed(v.to_glib_none_mut().0, ptr::null_mut()); + } + } +} + +impl GlibPtrDefault for CapsFeatures { + type GlibType = *mut ffi::GstCapsFeatures; +} + +#[repr(C)] +pub struct CapsFeaturesRef(ffi::GstCapsFeatures); + +impl CapsFeaturesRef { + pub unsafe fn from_glib_borrow<'a>(ptr: *const ffi::GstCapsFeatures) -> &'a CapsFeaturesRef { + assert!(!ptr.is_null()); + + &*(ptr as *mut CapsFeaturesRef) + } + + pub unsafe fn from_glib_borrow_mut<'a>( + ptr: *mut ffi::GstCapsFeatures, + ) -> &'a mut CapsFeaturesRef { + assert!(!ptr.is_null()); + + &mut *(ptr as *mut CapsFeaturesRef) + } + + pub unsafe fn as_ptr(&self) -> *const ffi::GstCapsFeatures { + self as *const Self as *const ffi::GstCapsFeatures + } + + pub unsafe fn as_mut_ptr(&self) -> *mut ffi::GstCapsFeatures { + self as *const Self as *mut ffi::GstCapsFeatures + } + + pub fn to_string(&self) -> String { + unsafe { from_glib_full(ffi::gst_caps_features_to_string(self.as_ptr())) } + } + + pub fn is_empty(&self) -> bool { + self.get_size() == 0 && !self.is_any() + } + + pub fn is_any(&self) -> bool { + unsafe { from_glib(ffi::gst_caps_features_is_any(self.as_ptr())) } + } + + pub fn contains(&self, feature: &str) -> bool { + unsafe { + from_glib(ffi::gst_caps_features_contains( + self.as_ptr(), + feature.to_glib_none().0, + )) + } + } + + pub fn get_size(&self) -> u32 { + unsafe { ffi::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 = ffi::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 { ffi::gst_caps_features_add(self.as_mut_ptr(), feature.to_glib_none().0) } + } + + pub fn remove(&mut self, feature: &str) { + unsafe { ffi::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(ffi::gst_caps_features_is_equal( + self.as_ptr(), + other.as_ptr(), + )) + } + } +} + +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 = ffi::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 = + ffi::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 { + f.write_str(&self.to_string()) + } +} + +impl ToOwned for CapsFeaturesRef { + type Owned = CapsFeatures; + + fn to_owned(&self) -> CapsFeatures { + unsafe { from_glib_full(ffi::gst_caps_features_copy(self.as_ptr() as *const _) as *mut _) } + } +} + +unsafe impl Sync for CapsFeaturesRef {} +unsafe impl Send for CapsFeaturesRef {} + +lazy_static! { + pub static ref CAPS_FEATURE_MEMORY_SYSTEM_MEMORY: &'static str = unsafe { + CStr::from_ptr(ffi::GST_CAPS_FEATURE_MEMORY_SYSTEM_MEMORY) + .to_str() + .unwrap() + }; + pub static ref CAPS_FEATURES_MEMORY_SYSTEM_MEMORY: CapsFeatures = + CapsFeatures::new(&[*CAPS_FEATURE_MEMORY_SYSTEM_MEMORY]); +} diff --git a/gstreamer/src/caps_features_serde.rs b/gstreamer/src/caps_features_serde.rs new file mode 100644 index 000000000..83d32d096 --- /dev/null +++ b/gstreamer/src/caps_features_serde.rs @@ -0,0 +1,166 @@ +// 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 serde::de; +use serde::de::{Deserialize, Deserializer, EnumAccess, SeqAccess, VariantAccess, Visitor}; +use serde::ser::{Serialize, SerializeSeq, Serializer}; + +use std::fmt; + +use CapsFeatures; +use CapsFeaturesRef; + +enum CapsFeaturesVariantKinds { + Any, + Some, +} + +const CAPS_FEATURES_VARIANT_ANY_ID: u32 = 0; +const CAPS_FEATURES_VARIANT_ANY_STR: &str = "Any"; +const CAPS_FEATURES_VARIANT_SOME_ID: u32 = 1; +const CAPS_FEATURES_VARIANT_SOME_STR: &str = "Some"; + +const CAPS_FEATURES_VARIANT_NAMES: &[&str] = &[ + &CAPS_FEATURES_VARIANT_ANY_STR, + &CAPS_FEATURES_VARIANT_SOME_STR, +]; + +struct CapsFeaturesForIterSe<'a>(&'a CapsFeaturesRef); +impl<'a> Serialize for CapsFeaturesForIterSe<'a> { + fn serialize(&self, serializer: S) -> Result { + let iter = self.0.iter(); + let size = iter.size_hint().0; + if size > 0 { + let mut seq = serializer.serialize_seq(Some(size))?; + for feature in iter { + seq.serialize_element(feature)?; + } + seq.end() + } else { + let seq = serializer.serialize_seq(None)?; + seq.end() + } + } +} + +impl Serialize for CapsFeaturesRef { + fn serialize(&self, serializer: S) -> Result { + if self.is_any() { + serializer.serialize_unit_variant( + stringify!(CapsFeatures), + CAPS_FEATURES_VARIANT_ANY_ID, + CAPS_FEATURES_VARIANT_ANY_STR, + ) + } else { + serializer.serialize_newtype_variant( + stringify!(CapsFeatures), + CAPS_FEATURES_VARIANT_SOME_ID, + CAPS_FEATURES_VARIANT_SOME_STR, + &CapsFeaturesForIterSe(&self), + ) + } + } +} + +impl Serialize for CapsFeatures { + fn serialize(&self, serializer: S) -> Result { + self.as_ref().serialize(serializer) + } +} + +struct CapsFeaturesSome(CapsFeatures); + +struct CapsFeaturesSomeVisitor; +impl<'de> Visitor<'de> for CapsFeaturesSomeVisitor { + type Value = CapsFeaturesSome; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("a sequence of `&str`") + } + + fn visit_seq>(self, mut seq: A) -> Result { + let mut features = CapsFeatures::new_empty(); + while let Some(feature) = seq.next_element::()? { + features.add(feature.as_ref()); + } + Ok(CapsFeaturesSome(features)) + } +} + +impl<'de> Deserialize<'de> for CapsFeaturesSome { + fn deserialize>(deserializer: D) -> Result { + deserializer.deserialize_seq(CapsFeaturesSomeVisitor) + } +} + +struct CapsFeaturesVariantKindsVisitor; +impl<'de> Visitor<'de> for CapsFeaturesVariantKindsVisitor { + type Value = CapsFeaturesVariantKinds; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("a Caps variant kind (`Any` or `Some`)") + } + + fn visit_u32(self, value: u32) -> Result { + match value { + CAPS_FEATURES_VARIANT_ANY_ID => Ok(CapsFeaturesVariantKinds::Any), + CAPS_FEATURES_VARIANT_SOME_ID => Ok(CapsFeaturesVariantKinds::Some), + _ => Err(de::Error::invalid_value( + de::Unexpected::Unsigned(value as u64), + &self, + )), + } + } + + fn visit_str(self, value: &str) -> Result { + match value { + CAPS_FEATURES_VARIANT_ANY_STR => Ok(CapsFeaturesVariantKinds::Any), + CAPS_FEATURES_VARIANT_SOME_STR => Ok(CapsFeaturesVariantKinds::Some), + _ => Err(de::Error::unknown_variant( + value, + CAPS_FEATURES_VARIANT_NAMES, + )), + } + } +} + +impl<'de> Deserialize<'de> for CapsFeaturesVariantKinds { + fn deserialize>(deserializer: D) -> Result { + deserializer.deserialize_identifier(CapsFeaturesVariantKindsVisitor) + } +} + +struct CapsFeaturesVisitor; +impl<'de> Visitor<'de> for CapsFeaturesVisitor { + type Value = CapsFeatures; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("a CapsFeatures enum (`Any` or `Some()`)") + } + + fn visit_enum>(self, data: A) -> Result { + let res = match data.variant()? { + (CapsFeaturesVariantKinds::Any, _v) => CapsFeatures::new_any(), + (CapsFeaturesVariantKinds::Some, v) => v + .newtype_variant::() + .map(|caps_features_some| caps_features_some.0)?, + }; + + Ok(res) + } +} + +impl<'de> Deserialize<'de> for CapsFeatures { + fn deserialize>(deserializer: D) -> Result { + deserializer.deserialize_enum( + stringify!(Caps), + CAPS_FEATURES_VARIANT_NAMES, + CapsFeaturesVisitor, + ) + } +} diff --git a/gstreamer/src/caps_serde.rs b/gstreamer/src/caps_serde.rs index 169bca2f5..3b126701c 100644 --- a/gstreamer/src/caps_serde.rs +++ b/gstreamer/src/caps_serde.rs @@ -13,6 +13,8 @@ use serde::ser::{Serialize, SerializeSeq, SerializeTuple, Serializer}; use std::fmt; use Caps; +use CapsFeatures; +use CapsFeaturesRef; use CapsRef; use Structure; use StructureRef; @@ -36,15 +38,12 @@ const CAPS_VARIANT_NAMES: &[&str] = &[ &CAPS_VARIANT_SOME_STR, ]; -// `CapsFeature` is not available in `gstreamer-rs` yet -struct CapsItemSe<'a>(&'a StructureRef); +struct CapsItemSe<'a>(&'a StructureRef, Option<&'a CapsFeaturesRef>); impl<'a> Serialize for CapsItemSe<'a> { fn serialize(&self, serializer: S) -> Result { let mut tup = serializer.serialize_tuple(2)?; tup.serialize_element(self.0)?; - // `CapsFeature` is not available in `gstreamer-rs` yet - // Fake the type for now and use `None` as a value - tup.serialize_element::>(&None)?; + tup.serialize_element(&self.1)?; tup.end() } } @@ -52,12 +51,19 @@ impl<'a> Serialize for CapsItemSe<'a> { struct CapsForIterSe<'a>(&'a CapsRef); impl<'a> Serialize for CapsForIterSe<'a> { fn serialize(&self, serializer: S) -> Result { - let iter = self.0.iter(); + let iter = self.0.iter_with_features(); let size = iter.size_hint().0; if size > 0 { let mut seq = serializer.serialize_seq(Some(size))?; - for structure in iter { - seq.serialize_element(&CapsItemSe(structure))?; + for (structure, features) in iter { + let features = if !features.is_any() && features + .is_equal(::CAPS_FEATURES_MEMORY_SYSTEM_MEMORY.as_ref()) + { + None + } else { + Some(features) + }; + seq.serialize_element(&CapsItemSe(structure, features))?; } seq.end() } else { @@ -98,13 +104,7 @@ impl Serialize for Caps { } } -// `CapsFeature` is not available in `gstreamer-rs` yet -struct CapsItemDe(Structure); -impl From for Structure { - fn from(caps_item: CapsItemDe) -> Structure { - caps_item.0 - } -} +struct CapsItemDe(Structure, Option); struct CapsItemVisitor; impl<'de> Visitor<'de> for CapsItemVisitor { @@ -118,20 +118,13 @@ impl<'de> Visitor<'de> for CapsItemVisitor { let structure = seq .next_element::()? .ok_or(de::Error::custom("Expected a `Structure` for `Caps` item"))?; - // `CapsFeature` is not available in `gstreamer-rs` yet - // Fake the type for now and expect `None` as a value - let feature_option = seq - .next_element::>()? - .ok_or(de::Error::custom( - "Expected an `Option` for `Caps` item", - ))?; - if feature_option.is_some() { - Err(de::Error::custom( - "Found a value for `CapsFeature`, expected `None` (not implemented yet)", - )) - } else { - Ok(CapsItemDe(structure)) - } + let features_option = + seq.next_element::>()? + .ok_or(de::Error::custom( + "Expected an `Option` for `Caps` item", + ))?; + + Ok(CapsItemDe(structure, features_option)) } } @@ -156,7 +149,7 @@ impl<'de> Visitor<'de> for CapsSomeVisitor { { let caps = caps.get_mut().unwrap(); while let Some(caps_item) = seq.next_element::()? { - caps.append_structure(caps_item.into()); + caps.append_structure_full(caps_item.0, caps_item.1); } } Ok(CapsSome(caps)) @@ -238,6 +231,7 @@ mod tests { use Array; use Caps; + use CapsFeatures; use Fraction; #[test] @@ -274,6 +268,71 @@ mod tests { res, ); + let caps = Caps::builder("foo/bar") + .field("int", &12) + .field("bool", &true) + .field("string", &"bla") + .field("fraction", &Fraction::new(1, 2)) + .field("array", &Array::new(&[&1, &2])) + .features(&["foo:bar", "foo:baz"]) + .build(); + + let mut pretty_config = ron::ser::PrettyConfig::default(); + pretty_config.new_line = "".to_string(); + + let res = ron::ser::to_string_pretty(&caps, pretty_config.clone()); + assert_eq!( + Ok(concat!( + "Some([", + " ((\"foo/bar\", [", + " (\"int\", \"i32\", 12),", + " (\"bool\", \"bool\", true),", + " (\"string\", \"String\", \"bla\"),", + " (\"fraction\", \"Fraction\", (1, 2)),", + " (\"array\", \"Array\", [", + " (\"i32\", 1),", + " (\"i32\", 2),", + " ]),", + " ]), Some(Some([", + " \"foo:bar\",", + " \"foo:baz\",", + " ]))),", + "])" + ).to_owned()), + res, + ); + + let caps = Caps::builder("foo/bar") + .field("int", &12) + .field("bool", &true) + .field("string", &"bla") + .field("fraction", &Fraction::new(1, 2)) + .field("array", &Array::new(&[&1, &2])) + .any_features() + .build(); + + let mut pretty_config = ron::ser::PrettyConfig::default(); + pretty_config.new_line = "".to_string(); + + let res = ron::ser::to_string_pretty(&caps, pretty_config.clone()); + assert_eq!( + Ok(concat!( + "Some([", + " ((\"foo/bar\", [", + " (\"int\", \"i32\", 12),", + " (\"bool\", \"bool\", true),", + " (\"string\", \"String\", \"bla\"),", + " (\"fraction\", \"Fraction\", (1, 2)),", + " (\"array\", \"Array\", [", + " (\"i32\", 1),", + " (\"i32\", 2),", + " ]),", + " ]), Some(Any)),", + "])" + ).to_owned()), + res, + ); + let caps_any = Caps::new_any(); let res = ron::ser::to_string_pretty(&caps_any, pretty_config.clone()); assert_eq!(Ok("Any".to_owned()), res); @@ -328,6 +387,74 @@ mod tests { ], ).as_ref() ); + + let caps_ron = r#" + Some([ + ( + ("foo/bar", [ + ("int", "i32", 12), + ("bool", "bool", true), + ("string", "String", "bla"), + ("fraction", "Fraction", (1, 2)), + ("array", "Array", [ + ("i32", 1), + ("i32", 2), + ]), + ]), + Some(Some(["foo:bar", "foo:baz"])), + ), + ])"#; + let caps: Caps = ron::de::from_str(caps_ron).unwrap(); + let s = caps.get_structure(0).unwrap(); + assert_eq!( + s, + Structure::new( + "foo/bar", + &[ + ("int", &12), + ("bool", &true), + ("string", &"bla"), + ("fraction", &Fraction::new(1, 2)), + ("array", &Array::new(&[&1, &2])), + ], + ).as_ref() + ); + let f = caps.get_features(0).unwrap(); + assert!(f.is_equal(CapsFeatures::new(&["foo:bar", "foo:baz"]).as_ref())); + + let caps_ron = r#" + Some([ + ( + ("foo/bar", [ + ("int", "i32", 12), + ("bool", "bool", true), + ("string", "String", "bla"), + ("fraction", "Fraction", (1, 2)), + ("array", "Array", [ + ("i32", 1), + ("i32", 2), + ]), + ]), + Some(Any), + ), + ])"#; + let caps: Caps = ron::de::from_str(caps_ron).unwrap(); + let s = caps.get_structure(0).unwrap(); + assert_eq!( + s, + Structure::new( + "foo/bar", + &[ + ("int", &12), + ("bool", &true), + ("string", &"bla"), + ("fraction", &Fraction::new(1, 2)), + ("array", &Array::new(&[&1, &2])), + ], + ).as_ref() + ); + let f = caps.get_features(0).unwrap(); + assert!(f.is_any()); } #[test] @@ -354,5 +481,29 @@ mod tests { let caps_ser = ron::ser::to_string(&caps).unwrap(); let caps_de: Caps = ron::de::from_str(caps_ser.as_str()).unwrap(); assert!(caps_de.is_strictly_equal(&caps)); + + let caps = Caps::builder("foo/bar") + .field("int", &12) + .field("bool", &true) + .field("string", &"bla") + .field("fraction", &Fraction::new(1, 2)) + .field("array", &Array::new(&[&1, &2])) + .features(&["foo:bar", "foo:baz"]) + .build(); + let caps_ser = ron::ser::to_string(&caps).unwrap(); + let caps_de: Caps = ron::de::from_str(caps_ser.as_str()).unwrap(); + assert!(caps_de.is_strictly_equal(&caps)); + + let caps = Caps::builder("foo/bar") + .field("int", &12) + .field("bool", &true) + .field("string", &"bla") + .field("fraction", &Fraction::new(1, 2)) + .field("array", &Array::new(&[&1, &2])) + .any_features() + .build(); + let caps_ser = ron::ser::to_string(&caps).unwrap(); + let caps_de: Caps = ron::de::from_str(caps_ser.as_str()).unwrap(); + assert!(caps_de.is_strictly_equal(&caps)); } } diff --git a/gstreamer/src/lib.rs b/gstreamer/src/lib.rs index 94a73a412..6ed51f0f6 100644 --- a/gstreamer/src/lib.rs +++ b/gstreamer/src/lib.rs @@ -91,6 +91,10 @@ pub mod caps; pub use caps::{Caps, CapsRef}; #[cfg(feature = "ser_de")] mod caps_serde; +mod caps_features; +pub use caps_features::{CapsFeatures, CapsFeaturesRef, CAPS_FEATURE_MEMORY_SYSTEM_MEMORY, CAPS_FEATURES_MEMORY_SYSTEM_MEMORY}; +#[cfg(feature = "ser_de")] +mod caps_features_serde; pub mod tags; pub use tags::{Tag, TagList, TagListRef};