From 2d97baaf96bea03d862aa4bf84cc5cef897888a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Mon, 17 Apr 2017 11:29:28 +0300 Subject: [PATCH] Rewrite GValue bindings --- gst-plugin/src/caps.rs | 6 +- gst-plugin/src/miniobject.rs | 9 +- gst-plugin/src/sink.rs | 3 +- gst-plugin/src/source.rs | 3 +- gst-plugin/src/tags.rs | 44 +- gst-plugin/src/value.rs | 925 ++++++++++++++++++++++++++--------- 6 files changed, 722 insertions(+), 268 deletions(-) diff --git a/gst-plugin/src/caps.rs b/gst-plugin/src/caps.rs index cf2b4c59..a5aca29d 100644 --- a/gst-plugin/src/caps.rs +++ b/gst-plugin/src/caps.rs @@ -13,7 +13,6 @@ use value::*; use miniobject::*; use glib; -use gobject; use gst; #[derive(Eq)] @@ -77,9 +76,8 @@ impl Caps { for value in values { let name_cstr = CString::new(value.0).unwrap(); unsafe { - let mut gvalue = value.1.to_gvalue(); - gst::gst_caps_set_value(self.0, name_cstr.as_ptr(), &gvalue); - gobject::g_value_unset(&mut gvalue); + let gvalue = value.1.as_ptr(); + gst::gst_caps_set_value(self.0, name_cstr.as_ptr(), gvalue); } } } diff --git a/gst-plugin/src/miniobject.rs b/gst-plugin/src/miniobject.rs index 1c918f66..4b5e6a10 100644 --- a/gst-plugin/src/miniobject.rs +++ b/gst-plugin/src/miniobject.rs @@ -152,20 +152,17 @@ impl<'a, T: MiniObject> From<&'a mut T> for GstRc { } } -#[repr(C)] -pub struct GstRefPtr(pub *mut T::PtrType); - #[derive(Hash, Debug, PartialEq, Eq, PartialOrd, Ord)] pub struct GstRef<'a, T: 'a + MiniObject> { obj: T, #[allow(dead_code)] - phantom: PhantomData<&'a GstRefPtr>, + phantom: PhantomData<&'a T>, } impl<'a, T: MiniObject> GstRef<'a, T> { - pub unsafe fn new(ptr: &'a GstRefPtr) -> GstRef<'a, T> { + pub unsafe fn new(ptr: *mut T::PtrType) -> GstRef<'a, T> { GstRef { - obj: T::new_from_ptr(ptr.0 as *mut T::PtrType), + obj: T::new_from_ptr(ptr), phantom: PhantomData, } } diff --git a/gst-plugin/src/sink.rs b/gst-plugin/src/sink.rs index 67d58941..4831ff05 100644 --- a/gst-plugin/src/sink.rs +++ b/gst-plugin/src/sink.rs @@ -271,10 +271,9 @@ unsafe extern "C" fn sink_render(ptr: *mut gst_base::GstBaseSink, -> gst::GstFlowReturn { let sink = &*(ptr as *const RsSink); let wrap: &SinkWrapper = &*sink.wrap; - let buffer = GstRefPtr(buffer); panic_to_error!(wrap, gst::GST_FLOW_ERROR, { - let buffer: GstRef = GstRef::new(&buffer); + let buffer: GstRef = GstRef::new(buffer); wrap.render(buffer.as_ref()) }) } diff --git a/gst-plugin/src/source.rs b/gst-plugin/src/source.rs index 1f1169e5..67e28023 100644 --- a/gst-plugin/src/source.rs +++ b/gst-plugin/src/source.rs @@ -330,10 +330,9 @@ unsafe extern "C" fn source_fill(ptr: *mut gst_base::GstBaseSrc, -> gst::GstFlowReturn { let src = &*(ptr as *const RsSrc); let wrap: &SourceWrapper = &*src.wrap; - let buffer = GstRefPtr(buffer); panic_to_error!(wrap, gst::GST_FLOW_ERROR, { - let mut buffer: GstRef = GstRef::new(&buffer); + let mut buffer: GstRef = GstRef::new(buffer); wrap.fill(offset, length, buffer.get_mut().unwrap()) }) } diff --git a/gst-plugin/src/tags.rs b/gst-plugin/src/tags.rs index e9cc532f..1432d9a1 100644 --- a/gst-plugin/src/tags.rs +++ b/gst-plugin/src/tags.rs @@ -16,15 +16,15 @@ use glib; use gobject; use gst; -pub trait Tag { - type TagType: ValueType; +pub trait Tag<'a> { + type TagType: ValueType<'a>; fn tag_name() -> &'static str; } macro_rules! impl_tag( ($name:ident, $t:ty, $tag:expr) => { pub struct $name; - impl Tag for $name { + impl<'a> Tag<'a> for $name { type TagType = $t; fn tag_name() -> &'static str { $tag @@ -33,16 +33,16 @@ macro_rules! impl_tag( }; ); -impl_tag!(Title, String, "title"); -impl_tag!(Album, String, "album"); -impl_tag!(Artist, String, "artist"); -impl_tag!(Encoder, String, "encoder"); -impl_tag!(AudioCodec, String, "audio-codec"); -impl_tag!(VideoCodec, String, "video-codec"); -impl_tag!(SubtitleCodec, String, "subtitle-codec"); -impl_tag!(ContainerFormat, String, "container-format"); +impl_tag!(Title, &'a str, "title"); +impl_tag!(Album, &'a str, "album"); +impl_tag!(Artist, &'a str, "artist"); +impl_tag!(Encoder, &'a str, "encoder"); +impl_tag!(AudioCodec, &'a str, "audio-codec"); +impl_tag!(VideoCodec, &'a str, "video-codec"); +impl_tag!(SubtitleCodec, &'a str, "subtitle-codec"); +impl_tag!(ContainerFormat, &'a str, "container-format"); // TODO: Should ideally enforce this to be ISO-639 -impl_tag!(LanguageCode, String, "language-code"); +impl_tag!(LanguageCode, &'a str, "language-code"); impl_tag!(Duration, u64, "duration"); impl_tag!(NominalBitrate, u32, "nominal-bitrate"); @@ -92,12 +92,12 @@ impl TagList { unsafe { GstRc::new_from_owned_ptr(gst::gst_tag_list_new_empty()) } } - pub fn add(&mut self, value: T::TagType, mode: MergeMode) - where Value: From<::TagType> + pub fn add<'a, T: Tag<'a>>(&mut self, value: T::TagType, mode: MergeMode) + where Value: From<>::TagType> { unsafe { let v = Value::from(value); - let mut gvalue = v.to_gvalue(); + let mut gvalue = v.into_raw(); let tag_name = CString::new(T::tag_name()).unwrap(); gst::gst_tag_list_add_value(self.0, mode.to_ffi(), tag_name.as_ptr(), &gvalue); @@ -106,8 +106,8 @@ impl TagList { } } - pub fn get(&self) -> Option> - where Value: From<::TagType> + pub fn get<'a, T: Tag<'a>>(&self) -> Option> + where Value: From<>::TagType> { unsafe { let mut gvalue = mem::zeroed(); @@ -119,13 +119,11 @@ impl TagList { return None; } - let res = match Value::from_gvalue(&gvalue) { - Some(value) => Some(TypedValue::new(value)), + let res = match Value::from_raw(gvalue) { + Some(value) => TypedValue::from_value(value), None => None, }; - gobject::g_value_unset(&mut gvalue); - res } } @@ -194,8 +192,8 @@ mod tests { tags.add::((1000u64 * 1000 * 1000 * 120).into(), MergeMode::Append); } - assert_eq!(*tags.get::().unwrap(), "some title"); - assert_eq!(*tags.get::<Duration>().unwrap(), + assert_eq!(tags.get::<Title>().unwrap().get(), "some title"); + assert_eq!(tags.get::<Duration>().unwrap().get(), (1000u64 * 1000 * 1000 * 120)); } } diff --git a/gst-plugin/src/value.rs b/gst-plugin/src/value.rs index 5b350118..51b8ff0f 100644 --- a/gst-plugin/src/value.rs +++ b/gst-plugin/src/value.rs @@ -6,10 +6,13 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use std::ffi::{CString, CStr}; +use std::ffi::CStr; use std::mem; use std::marker::PhantomData; -use std::ops::Deref; +use std::borrow::Cow; +use std::fmt; +use std::slice; +use libc::c_char; pub use num_rational::Rational32; @@ -20,45 +23,36 @@ use glib; use gobject; use gst; -#[derive(Debug, PartialEq, Eq, Clone)] -pub enum Value { +#[repr(C)] +pub struct Value(gobject::GValue); + +#[derive(Clone, PartialEq, Eq, Debug)] +pub enum ValueRef<'a> { Bool(bool), Int(i32), UInt(u32), Int64(i64), UInt64(u64), - String(String), + String(Cow<'a, str>), Fraction(Rational32), Buffer(GstRc<Buffer>), - Array(Vec<Value>), + Array(Cow<'a, [Value]>), } -pub trait ValueType { - fn extract(v: &Value) -> Option<&Self>; +impl<'a> ValueRef<'a> { + pub fn try_get<T: ValueType<'a>>(&'a self) -> Option<T> { + T::from_value_ref(&self) + } } -macro_rules! impl_value_type( - ($t:ty, $variant:ident) => { - impl ValueType for $t { - fn extract(v: &Value) -> Option<&$t> { - if let Value::$variant(ref b) = *v { - return Some(b); - } - None - } - } - }; -); +pub trait ValueType<'a> + where Self: Sized +{ + fn g_type() -> glib::GType; -impl_value_type!(bool, Bool); -impl_value_type!(i32, Int); -impl_value_type!(u32, UInt); -impl_value_type!(i64, Int64); -impl_value_type!(u64, UInt64); -impl_value_type!(String, String); -impl_value_type!(Rational32, Fraction); -impl_value_type!(GstRc<Buffer>, Buffer); -impl_value_type!(Vec<Value>, Array); + fn from_value(v: &'a Value) -> Option<Self>; + fn from_value_ref(v: &'a ValueRef<'a>) -> Option<Self>; +} lazy_static! { static ref TYPE_BUFFER: glib::GType = unsafe { gst::gst_buffer_get_type() }; @@ -67,278 +61,747 @@ lazy_static! { } impl Value { - pub unsafe fn to_gvalue(&self) -> gobject::GValue { - let mut gvalue = mem::zeroed(); - - match *self { - Value::Bool(v) => { - gobject::g_value_init(&mut gvalue, gobject::G_TYPE_BOOLEAN); - gobject::g_value_set_boolean(&mut gvalue, - if v { glib::GTRUE } else { glib::GFALSE }); - } - Value::Int(v) => { - gobject::g_value_init(&mut gvalue, gobject::G_TYPE_INT); - gobject::g_value_set_int(&mut gvalue, v); - } - Value::UInt(v) => { - gobject::g_value_init(&mut gvalue, gobject::G_TYPE_UINT); - gobject::g_value_set_uint(&mut gvalue, v); - } - Value::Int64(v) => { - gobject::g_value_init(&mut gvalue, gobject::G_TYPE_INT64); - gobject::g_value_set_int64(&mut gvalue, v); - } - Value::UInt64(v) => { - gobject::g_value_init(&mut gvalue, gobject::G_TYPE_UINT64); - gobject::g_value_set_uint64(&mut gvalue, v); - } - Value::String(ref v) => { - let v_cstr = CString::new(String::from(v.clone())).unwrap(); - - gobject::g_value_init(&mut gvalue, gobject::G_TYPE_STRING); - gobject::g_value_set_string(&mut gvalue, v_cstr.as_ptr()); - } - Value::Fraction(ref v) => { - gobject::g_value_init(&mut gvalue, *TYPE_FRACTION); - gst::gst_value_set_fraction(&mut gvalue, *v.numer(), *v.denom()); - } - Value::Buffer(ref buffer) => { - gobject::g_value_init(&mut gvalue, *TYPE_BUFFER); - gobject::g_value_set_boxed(&mut gvalue, buffer.as_ptr() as glib::gconstpointer); - } - Value::Array(ref array) => { - gobject::g_value_init(&mut gvalue, *TYPE_GST_VALUE_ARRAY); - - for e in array { - let mut e_value = e.to_gvalue(); - gst::gst_value_array_append_and_take_value(&mut gvalue, &mut e_value); - } - } - } - - gvalue + pub unsafe fn as_ptr(&self) -> *const gobject::GValue { + &self.0 } - pub unsafe fn from_gvalue(gvalue: &gobject::GValue) -> Option<Self> { - match gvalue.g_type { - gobject::G_TYPE_BOOLEAN => { - Some(Value::Bool(!(gobject::g_value_get_boolean(gvalue) == 0))) - } - gobject::G_TYPE_INT => Some(Value::Int(gobject::g_value_get_int(gvalue))), - gobject::G_TYPE_UINT => Some(Value::UInt(gobject::g_value_get_uint(gvalue))), - gobject::G_TYPE_INT64 => Some(Value::Int64(gobject::g_value_get_int64(gvalue))), - gobject::G_TYPE_UINT64 => Some(Value::UInt64(gobject::g_value_get_uint64(gvalue))), - gobject::G_TYPE_STRING => { - let s = gobject::g_value_get_string(gvalue); - if s.is_null() { - return None; - } + pub unsafe fn from_ptr(ptr: *const gobject::GValue) -> Option<Value> { + if ptr.is_null() || !Value::is_supported_type((*ptr).g_type) { + return None; + } - let cstr = CStr::from_ptr(s); - match cstr.to_str() { - Err(_) => None, - Ok(s) => Some(Value::String(s.into())), - } - } + let mut value = Value(mem::zeroed()); + gobject::g_value_init(&mut value.0, (*ptr).g_type); + gobject::g_value_copy(ptr, &mut value.0); + + Some(value) + } + + pub unsafe fn from_raw(value: gobject::GValue) -> Option<Value> { + if !Value::is_supported_type(value.g_type) { + return None; + } + Some(Value(value)) + } + + pub unsafe fn into_raw(mut self) -> gobject::GValue { + mem::replace(&mut self.0, mem::zeroed()) + } + + fn is_supported_type(typ: glib::GType) -> bool { + match typ { + gobject::G_TYPE_BOOLEAN | + gobject::G_TYPE_INT | + gobject::G_TYPE_UINT | + gobject::G_TYPE_INT64 | + gobject::G_TYPE_UINT64 | + gobject::G_TYPE_STRING => true, + typ if typ == *TYPE_FRACTION => true, + //typ if typ == *TYPE_BUFFER => true + typ if typ == *TYPE_GST_VALUE_ARRAY => true, + _ => false, + } + } + + pub fn new<T: Into<Value>>(v: T) -> Value { + v.into() + } + + pub fn new_from_value_ref(v: ValueRef) -> Value { + match v { + ValueRef::Bool(v) => Value::from(v), + ValueRef::Int(v) => Value::from(v), + ValueRef::UInt(v) => Value::from(v), + ValueRef::Int64(v) => Value::from(v), + ValueRef::UInt64(v) => Value::from(v), + ValueRef::Fraction(v) => Value::from(v), + ValueRef::String(v) => Value::from(v), + ValueRef::Array(v) => Value::from(v), + ValueRef::Buffer(v) => Value::from(v), + } + } + + pub fn get(&self) -> ValueRef { + match self.0.g_type { + gobject::G_TYPE_BOOLEAN => ValueRef::Bool(bool::from_value(&self).unwrap()), + gobject::G_TYPE_INT => ValueRef::Int(i32::from_value(&self).unwrap()), + gobject::G_TYPE_UINT => ValueRef::UInt(u32::from_value(&self).unwrap()), + gobject::G_TYPE_INT64 => ValueRef::Int64(i64::from_value(&self).unwrap()), + gobject::G_TYPE_UINT64 => ValueRef::UInt64(u64::from_value(&self).unwrap()), typ if typ == *TYPE_FRACTION => { - let n = gst::gst_value_get_fraction_numerator(gvalue); - let d = gst::gst_value_get_fraction_denominator(gvalue); - - Some(Value::Fraction(Rational32::new(n, d))) + ValueRef::Fraction(Rational32::from_value(&self).unwrap()) } - typ if typ == *TYPE_BUFFER => { - let b = gobject::g_value_get_boxed(gvalue); - - if b.is_null() { - return None; - } - - Some(Value::Buffer(GstRc::new_from_unowned_ptr(b as *mut gst::GstBuffer))) + gobject::G_TYPE_STRING => { + ValueRef::String(Cow::Borrowed(<&str as ValueType>::from_value(&self).unwrap())) } typ if typ == *TYPE_GST_VALUE_ARRAY => { - let n = gst::gst_value_array_get_size(gvalue); - - let mut vec = Vec::with_capacity(n as usize); - - for i in 0..n { - let val = gst::gst_value_array_get_value(gvalue, i); - - if val.is_null() { - return None; - } - - if let Some(val) = Value::from_gvalue(&*val) { - vec.push(val); - } else { - return None; - } - } - - Some(Value::Array(vec)) + ValueRef::Array(Cow::Borrowed(<&[Value] as ValueType>::from_value(&self).unwrap())) } - _ => None, + typ if typ == *TYPE_BUFFER => { + ValueRef::Buffer(<GstRc<Buffer> as ValueType>::from_value(&self).unwrap()) + } + _ => unreachable!(), } } - pub fn try_get<T: ValueType>(&self) -> Option<&T> { - T::extract(self) + pub fn try_get<'a, T: ValueType<'a>>(&'a self) -> Option<T> { + T::from_value(self) } } -impl From<bool> for Value { - fn from(f: bool) -> Value { - Value::Bool(f) +impl Clone for Value { + fn clone(&self) -> Self { + unsafe { + let mut new_value = Value(mem::zeroed()); + gobject::g_value_init(&mut new_value.0, self.0.g_type); + gobject::g_value_copy(&self.0, &mut new_value.0); + + new_value + } } } -impl From<i32> for Value { - fn from(f: i32) -> Value { - Value::Int(f) +impl PartialEq for Value { + fn eq(&self, other: &Value) -> bool { + self.get().eq(&other.get()) + } +} +impl Eq for Value {} + +impl fmt::Debug for Value { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.get().fmt(f) } } -impl From<u32> for Value { - fn from(f: u32) -> Value { - Value::UInt(f) +impl Drop for Value { + fn drop(&mut self) { + unsafe { + if self.0.g_type != gobject::G_TYPE_NONE { + gobject::g_value_unset(&mut self.0); + } + } } } -impl From<i64> for Value { - fn from(f: i64) -> Value { - Value::Int64(f) +macro_rules! impl_value_type_simple( + ($typ:ty, $variant:ident, $g_type:expr, $getter:expr, $setter:expr) => { + impl<'a> ValueType<'a> for $typ { + fn g_type() -> glib::GType { + $g_type + } + + fn from_value(value: &'a Value) -> Option<Self> { + if value.0.g_type != Self::g_type() { + return None; + } + + unsafe { + Some($getter(value)) + } + } + + fn from_value_ref(value_ref: &'a ValueRef<'a>) -> Option<Self> { + if let ValueRef::$variant(ref v) = *value_ref { + Some(*v) + } else { + None + } + } + } + + impl From<$typ> for Value { + fn from(v: $typ) -> Value { + unsafe { + let mut value = Value(mem::zeroed()); + + gobject::g_value_init(&mut value.0, <$typ as ValueType>::g_type()); + $setter(&mut value, v); + + value + } + } + } + }; +); + +impl_value_type_simple!(bool, + Bool, + gobject::G_TYPE_BOOLEAN, + |value: &Value| !(gobject::g_value_get_boolean(&value.0) == 0), + |value: &mut Value, v| { + gobject::g_value_set_boolean(&mut value.0, + if v { glib::GTRUE } else { glib::GFALSE }) + }); +impl_value_type_simple!(i32, + Int, + gobject::G_TYPE_INT, + |value: &Value| gobject::g_value_get_int(&value.0), + |value: &mut Value, v| gobject::g_value_set_int(&mut value.0, v)); +impl_value_type_simple!(u32, + UInt, + gobject::G_TYPE_UINT, + |value: &Value| gobject::g_value_get_uint(&value.0), + |value: &mut Value, v| gobject::g_value_set_uint(&mut value.0, v)); +impl_value_type_simple!(i64, + Int64, + gobject::G_TYPE_INT64, + |value: &Value| gobject::g_value_get_int64(&value.0), + |value: &mut Value, v| gobject::g_value_set_int64(&mut value.0, v)); +impl_value_type_simple!(u64, + UInt64, + gobject::G_TYPE_UINT64, + |value: &Value| gobject::g_value_get_uint64(&value.0), + |value: &mut Value, v| gobject::g_value_set_uint64(&mut value.0, v)); +impl_value_type_simple!(Rational32, + Fraction, + *TYPE_FRACTION, + |value: &Value| { + Rational32::new(gst::gst_value_get_fraction_numerator(&value.0), + gst::gst_value_get_fraction_denominator(&value.0)) + }, + |value: &mut Value, v: Rational32| { + gst::gst_value_set_fraction(&mut value.0, *v.numer(), *v.denom()) + }); + +impl<'a> ValueType<'a> for &'a str { + fn g_type() -> glib::GType { + gobject::G_TYPE_STRING + } + + fn from_value(value: &'a Value) -> Option<Self> { + if value.0.g_type != Self::g_type() { + return None; + } + + unsafe { + let s = gobject::g_value_get_string(&value.0); + if s.is_null() { + return Some(&""); + } + + let cstr = CStr::from_ptr(s).to_str().expect("Invalid string"); + Some(cstr) + } + } + + fn from_value_ref(value_ref: &'a ValueRef<'a>) -> Option<Self> { + if let ValueRef::String(ref v) = *value_ref { + Some(v.as_ref()) + } else { + None + } } } -impl From<u64> for Value { - fn from(f: u64) -> Value { - Value::UInt64(f) +impl<'a> From<Cow<'a, str>> for Value { + fn from(v: Cow<'a, str>) -> Value { + unsafe { + let mut value = Value(mem::zeroed()); + + gobject::g_value_init(&mut value.0, <&str as ValueType>::g_type()); + let v_cstr = glib::g_strndup(v.as_ptr() as *const c_char, v.len()); + gobject::g_value_take_string(&mut value.0, v_cstr); + + value + } } } impl From<String> for Value { - fn from(f: String) -> Value { - Value::String(f) + fn from(v: String) -> Value { + Value::from(Cow::Owned::<str>(v)) } } impl<'a> From<&'a str> for Value { - fn from(f: &'a str) -> Value { - Value::String(f.into()) + fn from(v: &'a str) -> Value { + Value::from(Cow::Borrowed::<str>(v)) } } -impl From<Rational32> for Value { - fn from(f: Rational32) -> Value { - Value::Fraction(f) +impl<'a> ValueType<'a> for GstRc<Buffer> { + fn g_type() -> glib::GType { + *TYPE_BUFFER + } + + fn from_value(value: &'a Value) -> Option<Self> { + if value.0.g_type != Self::g_type() { + return None; + } + + unsafe { + let buffer = gobject::g_value_get_boxed(&value.0) as *mut gst::GstBuffer; + Some(GstRc::new_from_unowned_ptr(buffer)) + } + } + + fn from_value_ref(value_ref: &'a ValueRef<'a>) -> Option<Self> { + if let ValueRef::Buffer(ref v) = *value_ref { + Some(v.clone()) + } else { + None + } + } +} + +impl From<GstRc<Buffer>> for Value { + fn from(v: GstRc<Buffer>) -> Value { + Value::from(v.as_ref()) + } +} + +impl<'a> From<&'a GstRc<Buffer>> for Value { + fn from(v: &'a GstRc<Buffer>) -> Value { + Value::from(v.as_ref()) + } +} + +impl<'a> From<&'a Buffer> for Value { + fn from(v: &'a Buffer) -> Value { + unsafe { + let mut value = Value(mem::zeroed()); + + gobject::g_value_init(&mut value.0, <GstRc<Buffer> as ValueType>::g_type()); + gobject::g_value_set_boxed(&mut value.0, v.as_ptr() as glib::gpointer); + + value + } + } +} + +impl<'a> ValueType<'a> for &'a [Value] { + fn g_type() -> glib::GType { + *TYPE_GST_VALUE_ARRAY + } + + fn from_value(value: &'a Value) -> Option<Self> { + if value.0.g_type != Self::g_type() { + return None; + } + + unsafe { + let arr = value.0.data[0] as *const glib::GArray; + + if arr.is_null() { + Some(&[]) + } else { + let arr = &*arr; + Some(slice::from_raw_parts(arr.data as *const Value, arr.len as usize)) + } + } + } + + fn from_value_ref(value_ref: &'a ValueRef<'a>) -> Option<Self> { + if let ValueRef::Array(ref v) = *value_ref { + Some(v.as_ref()) + } else { + None + } + } +} + +impl<'a> From<Cow<'a, [Value]>> for Value { + fn from(v: Cow<'a, [Value]>) -> Value { + unsafe { + let mut value = Value(mem::zeroed()); + + gobject::g_value_init(&mut value.0, <&[Value] as ValueType>::g_type()); + + match v { + Cow::Borrowed(ref array) => { + for e in *array { + gst::gst_value_array_append_value(&mut value.0, + e.as_ptr() as *mut gobject::GValue); + } + } + Cow::Owned(array) => { + for mut e in array { + gst::gst_value_array_append_and_take_value(&mut value.0, + e.as_ptr() as + *mut gobject::GValue); + e.0.g_type = gobject::G_TYPE_NONE; + } + } + } + + value + } + } +} + +impl From<Vec<Value>> for Value { + fn from(v: Vec<Value>) -> Value { + Value::from(Cow::Owned::<[Value]>(v)) + } +} + +impl<'a> From<&'a Vec<Value>> for Value { + fn from(v: &'a Vec<Value>) -> Value { + Value::from(Cow::Borrowed::<[Value]>(v.as_ref())) + } +} + +impl<'a> From<&'a [Value]> for Value { + fn from(v: &'a [Value]) -> Value { + Value::from(Cow::Borrowed::<[Value]>(v)) + } +} + +impl<'a> From<ValueRef<'a>> for Value { + fn from(value_ref: ValueRef<'a>) -> Value { + Value::new_from_value_ref(value_ref) + } +} + +impl<'a> From<&'a ValueRef<'a>> for Value { + fn from(value_ref: &'a ValueRef<'a>) -> Value { + Value::new_from_value_ref(value_ref.clone()) } } impl From<(i32, i32)> for Value { fn from((f_n, f_d): (i32, i32)) -> Value { - Value::Fraction(Rational32::new(f_n, f_d)) + Value::from(Rational32::new(f_n, f_d)) } } -impl From<GstRc<Buffer>> for Value { - fn from(f: GstRc<Buffer>) -> Value { - Value::Buffer(f) - } -} - -impl<'a> From<&'a Buffer> for Value { - fn from(f: &'a Buffer) -> Value { - Value::Buffer(f.into()) - } -} - -impl From<Vec<Value>> for Value { - fn from(f: Vec<Value>) -> Value { - Value::Array(f) - } -} - -impl<'a> From<&'a [Value]> for Value { - fn from(f: &'a [Value]) -> Value { - Value::Array(f.to_vec()) - } -} - -#[derive(Debug, PartialEq, Eq, Clone)] -pub struct TypedValue<T> - where T: ValueType -{ +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct TypedValue<T> { value: Value, phantom: PhantomData<T>, } -impl<T> TypedValue<T> - where T: ValueType +impl<'a, T> TypedValue<T> + where T: ValueType<'a> { - pub fn new(value: Value) -> Self { - TypedValue { - value: value, - phantom: PhantomData, + pub fn new<VT: Into<TypedValue<T>>>(v: VT) -> TypedValue<T> { + v.into() + } + + pub fn from_value(value: Value) -> Option<TypedValue<T>> { + if value.0.g_type != T::g_type() { + return None; } + + Some(TypedValue { + value: value, + phantom: PhantomData, + }) + } + + pub fn get(&'a self) -> T { + self.value.try_get::<T>().unwrap() } pub fn into_value(self) -> Value { self.value } -} -impl<T> Deref for TypedValue<T> - where T: ValueType -{ - type Target = T; - fn deref(&self) -> &T { - self.value.try_get().unwrap() + pub unsafe fn as_ptr(&self) -> *const gobject::GValue { + &self.value.0 + } + + pub unsafe fn from_ptr(ptr: *const gobject::GValue) -> Option<TypedValue<T>> { + if let Some(value) = Value::from_ptr(ptr) { + return TypedValue::from_value(value); + } + None + } + + pub unsafe fn from_raw(value: gobject::GValue) -> Option<TypedValue<T>> { + if let Some(value) = Value::from_raw(value) { + return TypedValue::from_value(value); + } + None + } + + pub unsafe fn into_raw(mut self) -> gobject::GValue { + mem::replace(&mut self.value.0, mem::zeroed()) } } -impl<T> AsRef<T> for TypedValue<T> - where T: ValueType +impl<'a, T> From<T> for TypedValue<T> + where T: ValueType<'a> + Into<Value> { - fn as_ref(&self) -> &T { - self.value.try_get().unwrap() + fn from(v: T) -> Self { + TypedValue::from_value(Value::new(v)).unwrap() } } -#[derive(Debug, PartialEq, Eq, Clone)] -pub struct TypedValueRef<'a, T> - where T: ValueType -{ - value: &'a Value, - phantom: PhantomData<T>, +impl<'a> From<Cow<'a, str>> for TypedValue<&'a str> { + fn from(v: Cow<'a, str>) -> Self { + TypedValue::from_value(Value::new(v)).unwrap() + } } -impl<'a, T> TypedValueRef<'a, T> - where T: ValueType -{ - pub fn new(value: &'a Value) -> TypedValueRef<'a, T> { - TypedValueRef { - value: value, - phantom: PhantomData, +impl<'a> From<String> for TypedValue<&'a str> { + fn from(v: String) -> Self { + TypedValue::from_value(Value::new(v)).unwrap() + } +} + +impl<'a> From<Vec<Value>> for TypedValue<&'a [Value]> { + fn from(v: Vec<Value>) -> Self { + TypedValue::from_value(Value::new(v)).unwrap() + } +} + +impl<'a> From<&'a Vec<Value>> for TypedValue<&'a [Value]> { + fn from(v: &'a Vec<Value>) -> Self { + TypedValue::from_value(Value::new(v)).unwrap() + } +} + +impl<'a> From<Cow<'a, [Value]>> for TypedValue<&'a [Value]> { + fn from(v: Cow<'a, [Value]>) -> Self { + TypedValue::from_value(Value::new(v)).unwrap() + } +} + +impl<'a> From<&'a GstRc<Buffer>> for TypedValue<GstRc<Buffer>> { + fn from(v: &'a GstRc<Buffer>) -> Self { + TypedValue::from_value(Value::new(v)).unwrap() + } +} + +impl<'a> From<&'a Buffer> for TypedValue<GstRc<Buffer>> { + fn from(v: &'a Buffer) -> Self { + TypedValue::from_value(Value::new(v)).unwrap() + } +} + +#[cfg(test)] +mod tests { + use super::*; + use std::ptr; + + macro_rules! gen_test_value( + ($name: ident, $typ:ty, $value:expr, $variant:ident) => { + #[test] + fn $name() { + unsafe { gst::gst_init(ptr::null_mut(), ptr::null_mut()) }; + + let value = Value::new($value); + if let ValueRef::$variant(v) = value.get() { + assert_eq!(v, $value); + } else { + unreachable!(); + } + + if let Some(v) = value.get().try_get::<$typ>() { + assert_eq!(v, $value); + } else { + unreachable!(); + } + + let value2 = value.clone(); + if let ValueRef::$variant(v) = value2.get() { + assert_eq!(v, $value); + } else { + unreachable!(); + } + + let value2 = Value::new_from_value_ref(value.get()); + assert_eq!(value2, value); + + let value3 = TypedValue::new($value); + assert_eq!(value3.get(), $value); + + if let Some(value3) = TypedValue::<$typ>::from_value(value) { + assert_eq!(value3.get(), $value); + } else { + unreachable!(); + } + } + }; + ); + + gen_test_value!(int, i32, 12i32, Int); + gen_test_value!(uint, u32, 12u32, UInt); + gen_test_value!(int64, i64, 12i64, Int64); + gen_test_value!(uint64, u64, 12u64, UInt64); + gen_test_value!(boolean, bool, true, Bool); + gen_test_value!(fraction, Rational32, Rational32::new(1, 2), Fraction); + + #[test] + fn string_owned() { + unsafe { gst::gst_init(ptr::null_mut(), ptr::null_mut()) }; + + let orig_v = String::from("foo"); + + let value = Value::new(orig_v.clone()); + if let ValueRef::String(v) = value.get() { + assert_eq!(v, orig_v); + } else { + unreachable!(); + } + + if let Some(v) = value.get().try_get::<&str>() { + assert_eq!(v, orig_v); + } else { + unreachable!(); + } + + let value2 = value.clone(); + if let ValueRef::String(v) = value2.get() { + assert_eq!(v, orig_v); + } else { + unreachable!(); + } + + let value2 = Value::new_from_value_ref(value.get()); + assert_eq!(value2, value); + + + let value2 = Value::new_from_value_ref(value.get()); + assert_eq!(value2, value); + + let value3 = TypedValue::new(orig_v.clone()); + assert_eq!(value3.get(), orig_v.as_str()); + + if let Some(value3) = TypedValue::<&str>::from_value(value) { + assert_eq!(value3.get(), orig_v.as_str()); + } else { + unreachable!(); } } - pub fn value(self) -> &'a Value { - self.value - } -} + #[test] + fn string_borrowed() { + unsafe { gst::gst_init(ptr::null_mut(), ptr::null_mut()) }; -impl<'a, T> Deref for TypedValueRef<'a, T> - where T: ValueType -{ - type Target = T; - fn deref(&self) -> &T { - self.value.try_get().unwrap() - } -} + let orig_v = "foo"; -impl<'a, T> AsRef<T> for TypedValueRef<'a, T> - where T: ValueType -{ - fn as_ref(&self) -> &T { - self.value.try_get().unwrap() + let value = Value::new(orig_v); + if let ValueRef::String(v) = value.get() { + assert_eq!(v, orig_v); + } else { + unreachable!(); + } + + if let Some(v) = value.get().try_get::<&str>() { + assert_eq!(v, orig_v); + } else { + unreachable!(); + } + + let value2 = value.clone(); + if let ValueRef::String(v) = value2.get() { + assert_eq!(v, orig_v); + } else { + unreachable!(); + } + + let value2 = Value::new_from_value_ref(value.get()); + assert_eq!(value2, value); + + let value3 = TypedValue::new(orig_v); + assert_eq!(value3.get(), orig_v); + + if let Some(value3) = TypedValue::<&str>::from_value(value) { + assert_eq!(value3.get(), orig_v); + } else { + unreachable!(); + } + } + + #[test] + fn array_owned() { + unsafe { gst::gst_init(ptr::null_mut(), ptr::null_mut()) }; + + let orig_v = vec![Value::new("a"), Value::new("b")]; + + let value = Value::new(orig_v.clone()); + if let ValueRef::Array(arr) = value.get() { + assert_eq!(arr, orig_v.as_slice()); + } else { + unreachable!(); + } + + if let Some(v) = value.get().try_get::<&[Value]>() { + assert_eq!(v, orig_v.as_slice()); + } else { + unreachable!(); + } + + let value2 = Value::new_from_value_ref(value.get()); + assert_eq!(value2, value); + + let value2 = Value::new_from_value_ref(value.get()); + assert_eq!(value2, value); + + let value3 = TypedValue::new(orig_v.clone()); + assert_eq!(value3.get(), orig_v.as_slice()); + + if let Some(value3) = TypedValue::<&[Value]>::from_value(value) { + assert_eq!(value3.get(), orig_v.as_slice()); + } else { + unreachable!(); + } + } + + #[test] + fn array_borrowed() { + unsafe { gst::gst_init(ptr::null_mut(), ptr::null_mut()) }; + + let orig_v = vec![Value::new("a"), Value::new("b")]; + + let value = Value::new(&orig_v); + if let ValueRef::Array(arr) = value.get() { + assert_eq!(arr, orig_v.as_slice()); + } else { + unreachable!(); + } + + if let Some(arr) = value.get().try_get::<&[Value]>() { + assert_eq!(arr, orig_v.as_slice()); + } else { + unreachable!(); + } + + let value2 = Value::new_from_value_ref(value.get()); + assert_eq!(value2, value); + + let value3 = TypedValue::new(orig_v.as_slice()); + assert_eq!(value3.get(), orig_v.as_slice()); + + if let Some(value3) = TypedValue::<&[Value]>::from_value(value) { + assert_eq!(value3.get(), orig_v.as_slice()); + } else { + unreachable!(); + } + } + + #[test] + fn buffer() { + unsafe { gst::gst_init(ptr::null_mut(), ptr::null_mut()) }; + + let orig_v = Buffer::new_from_vec(vec![1, 2, 3, 4]).unwrap(); + + let value = Value::new(orig_v.clone()); + if let ValueRef::Buffer(buf) = value.get() { + assert_eq!(buf, orig_v); + } else { + unreachable!(); + } + + if let Some(buf) = value.get().try_get::<GstRc<Buffer>>() { + assert_eq!(buf, orig_v); + } else { + unreachable!(); + } + + let value2 = Value::new_from_value_ref(value.get()); + assert_eq!(value2, value); + + let value3 = TypedValue::new(&orig_v); + assert_eq!(value3.get(), orig_v); + + if let Some(value3) = TypedValue::<GstRc<Buffer>>::from_value(value) { + assert_eq!(value3.get(), orig_v); + } else { + unreachable!(); + } } }