From cb23d20270f84215154584f8641ac74b7c17d8ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Sat, 29 Sep 2018 11:13:57 +0300 Subject: [PATCH] Implement support for buffer Metas Fixes https://github.com/sdroege/gstreamer-rs/issues/103 --- gstreamer/src/buffer.rs | 94 +++++++++++- gstreamer/src/lib.rs | 4 + gstreamer/src/meta.rs | 333 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 430 insertions(+), 1 deletion(-) create mode 100644 gstreamer/src/meta.rs diff --git a/gstreamer/src/buffer.rs b/gstreamer/src/buffer.rs index 74768cd68..508f41da3 100644 --- a/gstreamer/src/buffer.rs +++ b/gstreamer/src/buffer.rs @@ -15,13 +15,14 @@ use std::slice; use std::u64; use std::usize; +use meta::*; use miniobject::*; use BufferFlags; use ClockTime; use ffi; use glib; -use glib::translate::{from_glib, from_glib_full, from_glib_none, ToGlib, ToGlibPtr}; +use glib::translate::{from_glib, from_glib_full, from_glib_none, FromGlib, ToGlib, ToGlibPtr}; use glib_ffi; pub enum Readable {} @@ -367,8 +368,99 @@ impl BufferRef { pub fn set_flags(&mut self, flags: BufferFlags) { self.0.mini_object.flags = flags.bits(); } + + pub fn get_meta(&self) -> Option> { + unsafe { + let meta = ffi::gst_buffer_get_meta(self.as_mut_ptr(), T::get_meta_api().to_glib()); + if meta.is_null() { + None + } else { + Some(T::from_ptr( + self.as_ptr(), + meta as *const ::GstType, + )) + } + } + } + + pub fn get_meta_mut(&mut self) -> Option> { + unsafe { + let meta = ffi::gst_buffer_get_meta(self.as_mut_ptr(), T::get_meta_api().to_glib()); + if meta.is_null() { + None + } else { + Some(T::from_mut_ptr( + self.as_mut_ptr(), + meta as *mut ::GstType, + )) + } + } + } + + pub fn iter_meta(&self) -> MetaIter { + MetaIter::new(self) + } + + pub fn iter_meta_mut(&mut self) -> MetaIterMut { + MetaIterMut::new(self) + } } +macro_rules! define_iter( + ($name:ident, $typ:ty, $mtyp:ty, $from_ptr:expr) => { + pub struct $name<'a, T: MetaAPI + 'a> { + buffer: $typ, + state: glib_ffi::gpointer, + meta_api: glib::Type, + items: PhantomData<$mtyp>, + } + + impl<'a, T: MetaAPI> $name<'a, T> { + fn new(buffer: $typ) -> $name<'a, T> { + skip_assert_initialized!(); + + $name { + buffer, + state: ptr::null_mut(), + meta_api: T::get_meta_api(), + items: PhantomData, + } + } + } + + impl<'a, T: MetaAPI> Iterator for $name<'a, T> { + type Item = $mtyp; + + fn next(&mut self) -> Option { + loop { + unsafe { + let meta = ffi::gst_buffer_iterate_meta(self.buffer.as_mut_ptr(), &mut self.state); + + if meta.is_null() { + return None; + } else if self.meta_api == glib::Type::Invalid || glib::Type::from_glib((*(*meta).info).api) == self.meta_api { + let item = $from_ptr(self.buffer.as_mut_ptr(), meta); + return Some(item); + } + } + } + } + } + + impl<'a, T: MetaAPI> ExactSizeIterator for $name<'a, T> {} + } +); + +define_iter!(MetaIter, &'a BufferRef, MetaRef<'a, T>, |buffer, meta| { + T::from_ptr(buffer, meta as *const ::GstType) +}); +define_iter!( + MetaIterMut, + &'a mut BufferRef, + MetaRefMut<'a, T, ::meta::Iterated>, + |buffer, meta| T::from_mut_ptr(buffer, meta as *mut ::GstType) +); + impl fmt::Debug for BufferRef { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_struct("Buffer") diff --git a/gstreamer/src/lib.rs b/gstreamer/src/lib.rs index 8896ec6b5..71ce94cb7 100644 --- a/gstreamer/src/lib.rs +++ b/gstreamer/src/lib.rs @@ -105,6 +105,8 @@ pub use tags::{Tag, TagList, TagListRef}; #[cfg(feature = "ser_de")] mod tags_serde; +pub mod meta; +pub use meta::{Meta, MetaAPI, MetaRef, MetaRefMut, ParentBufferMeta}; pub mod buffer; pub use buffer::{ Buffer, BufferMap, BufferRef, MappedBuffer, BUFFER_COPY_ALL, BUFFER_COPY_METADATA, @@ -290,6 +292,8 @@ pub mod prelude { pub use auto::traits::*; + pub use meta::MetaAPI; + pub use bin::BinExtManual; pub use element::ElementExtManual; diff --git a/gstreamer/src/meta.rs b/gstreamer/src/meta.rs new file mode 100644 index 000000000..245474e42 --- /dev/null +++ b/gstreamer/src/meta.rs @@ -0,0 +1,333 @@ +// 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::fmt; +use std::marker::PhantomData; +use std::mem; +use std::ops; + +use miniobject::MiniObject; +use BufferRef; + +use ffi; +use glib; +use glib::translate::{from_glib, FromGlib}; +use glib_ffi; + +pub unsafe trait MetaAPI: Sized { + type GstType; + + fn get_meta_api() -> glib::Type; + + unsafe fn from_ptr<'a>( + buffer: *const ffi::GstBuffer, + ptr: *const Self::GstType, + ) -> MetaRef<'a, Self> { + assert!(!ptr.is_null()); + + let meta_api = Self::get_meta_api(); + if meta_api != glib::Type::Invalid { + assert_eq!( + meta_api, + from_glib((*(*(ptr as *const ffi::GstMeta)).info).api) + ) + } + + MetaRef { + meta: mem::transmute(ptr), + buffer, + } + } + + unsafe fn from_mut_ptr<'a, T>( + buffer: *mut ffi::GstBuffer, + ptr: *mut Self::GstType, + ) -> MetaRefMut<'a, Self, T> { + assert!(!ptr.is_null()); + + let meta_api = Self::get_meta_api(); + if meta_api != glib::Type::Invalid { + assert_eq!( + meta_api, + from_glib((*(*(ptr as *const ffi::GstMeta)).info).api) + ) + } + + MetaRefMut { + meta: mem::transmute(ptr), + buffer, + mode: PhantomData, + } + } +} + +#[derive(Debug)] +pub struct MetaRef<'a, T: MetaAPI + 'a> { + meta: &'a T, + buffer: *const ffi::GstBuffer, +} + +pub enum Standalone {} +pub enum Iterated {} + +#[derive(Debug)] +pub struct MetaRefMut<'a, T: MetaAPI + 'a, U> { + meta: &'a mut T, + buffer: *mut ffi::GstBuffer, + mode: PhantomData, +} + +impl<'a, T: MetaAPI> ops::Deref for MetaRef<'a, T> { + type Target = T; + + fn deref(&self) -> &T { + self.meta + } +} + +impl<'a, T: MetaAPI> AsRef> for MetaRef<'a, T> { + fn as_ref(&self) -> &MetaRef<'a, T> { + self + } +} + +impl<'a, T: MetaAPI, U> ops::Deref for MetaRefMut<'a, T, U> { + type Target = T; + + fn deref(&self) -> &T { + self.meta + } +} + +impl<'a, T: MetaAPI, U> ops::DerefMut for MetaRefMut<'a, T, U> { + fn deref_mut(&mut self) -> &mut T { + self.meta + } +} + +impl<'a, T: MetaAPI, U> AsRef> for MetaRefMut<'a, T, U> { + fn as_ref(&self) -> &MetaRef<'a, T> { + unsafe { mem::transmute(self) } + } +} + +impl<'a, T: MetaAPI> MetaRef<'a, T> { + pub fn get_api(&self) -> glib::Type { + unsafe { + let meta = self.meta as *const _ as *const ffi::GstMeta; + let info = (*meta).info; + glib::Type::from_glib((*info).api) + } + } + + pub fn as_ptr(&self) -> *const T::GstType { + self.meta as *const _ as *const ::GstType + } +} + +impl<'a> MetaRef<'a, Meta<'a>> { + pub fn downcast_ref(&self) -> Option<&MetaRef<'a, T>> { + let target_type = T::get_meta_api(); + let type_ = self.get_api(); + + if type_ == glib::Type::Invalid || target_type == type_ { + Some(unsafe { mem::transmute(self) }) + } else { + None + } + } +} + +impl<'a, T: MetaAPI, U> MetaRefMut<'a, T, U> { + pub fn get_api(&self) -> glib::Type { + unsafe { + let meta = self.meta as *const _ as *const ffi::GstMeta; + let info = (*meta).info; + glib::Type::from_glib((*info).api) + } + } + + pub fn as_ptr(&self) -> *const T::GstType { + self.meta as *const _ as *const ::GstType + } + + pub fn as_mut_ptr(&mut self) -> *mut T::GstType { + self.meta as *mut _ as *mut ::GstType + } +} + +impl<'a, T: MetaAPI> MetaRefMut<'a, T, Standalone> { + pub fn remove(mut self) { + unsafe { + let res = + ffi::gst_buffer_remove_meta(self.buffer, self.as_mut_ptr() as *mut ffi::GstMeta); + assert_ne!(res, glib_ffi::GFALSE); + } + } +} + +impl<'a, U> MetaRefMut<'a, Meta<'a>, U> { + pub fn downcast_ref(&mut self) -> Option<&MetaRefMut<'a, T, U>> { + let target_type = T::get_meta_api(); + let type_ = self.get_api(); + + if type_ == glib::Type::Invalid || target_type == type_ { + Some(unsafe { mem::transmute(self) }) + } else { + None + } + } +} + +#[repr(C)] +pub struct Meta<'a>(ffi::GstMeta, PhantomData<&'a ()>); + +impl<'a> Meta<'a> { + fn get_api(&self) -> glib::Type { + unsafe { glib::Type::from_glib((*self.0.info).api) } + } +} + +unsafe impl<'a> MetaAPI for Meta<'a> { + type GstType = ffi::GstMeta; + + fn get_meta_api() -> glib::Type { + glib::Type::Invalid + } +} + +impl<'a> fmt::Debug for Meta<'a> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("Meta") + .field("api", &self.get_api()) + .finish() + } +} + +#[repr(C)] +pub struct ParentBufferMeta<'a>(ffi::GstParentBufferMeta, PhantomData<&'a ()>); + +impl<'a> ParentBufferMeta<'a> { + pub fn add(buffer: &'a mut BufferRef, parent: &BufferRef) -> MetaRefMut<'a, Self, Standalone> { + unsafe { + let meta = + ffi::gst_buffer_add_parent_buffer_meta(buffer.as_mut_ptr(), parent.as_mut_ptr()); + + Self::from_mut_ptr(buffer.as_mut_ptr(), meta) + } + } + + pub fn get_parent(&self) -> &BufferRef { + unsafe { BufferRef::from_ptr(self.0.buffer) } + } +} + +unsafe impl<'a> MetaAPI for ParentBufferMeta<'a> { + type GstType = ffi::GstParentBufferMeta; + + fn get_meta_api() -> glib::Type { + unsafe { from_glib(ffi::gst_parent_buffer_meta_api_get_type()) } + } +} + +impl<'a> fmt::Debug for ParentBufferMeta<'a> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("ParentBufferMeta") + .field("parent", &self.get_parent()) + .finish() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_add_get_iterate_meta() { + gst::init().unwrap(); + + let mut buffer = ::Buffer::new(); + let parent = ::Buffer::new(); + { + let meta = ParentBufferMeta::add(buffer.get_mut().unwrap(), &*parent); + unsafe { + assert_eq!(meta.get_parent().as_ptr(), parent.as_ptr()); + } + } + + { + let metas = buffer.iter_meta::().collect::>(); + assert_eq!(metas.len(), 1); + } + { + let metas = buffer + .get_mut() + .unwrap() + .iter_meta_mut::() + .collect::>(); + assert_eq!(metas.len(), 1); + } + { + let metas = buffer.iter_meta::().collect::>(); + assert_eq!(metas.len(), 1); + unsafe { + assert_eq!(metas[0].get_parent().as_ptr(), parent.as_ptr()); + } + } + { + let metas = buffer + .get_mut() + .unwrap() + .iter_meta_mut::() + .collect::>(); + assert_eq!(metas.len(), 1); + unsafe { + assert_eq!(metas[0].get_parent().as_ptr(), parent.as_ptr()); + } + } + + { + let meta = buffer + .get_mut() + .unwrap() + .get_meta_mut::() + .unwrap(); + unsafe { + assert_eq!(meta.get_parent().as_ptr(), parent.as_ptr()); + } + meta.remove(); + } + + { + let metas = buffer.iter_meta::().collect::>(); + assert_eq!(metas.len(), 0); + } + { + let metas = buffer + .get_mut() + .unwrap() + .iter_meta_mut::() + .collect::>(); + assert_eq!(metas.len(), 0); + } + { + let metas = buffer.iter_meta::().collect::>(); + assert_eq!(metas.len(), 0); + } + { + let metas = buffer + .get_mut() + .unwrap() + .iter_meta_mut::() + .collect::>(); + assert_eq!(metas.len(), 0); + } + + assert!(buffer.get_meta::().is_none()); + } +}