// Take a look at the license at the top of the repository in the LICENSE file. use glib::translate::*; use gst::prelude::*; use std::marker::PhantomData; use crate::RelTypes; #[repr(transparent)] #[doc(alias = "GstAnalyticsRelationMeta")] pub struct AnalyticsRelationMeta(ffi::GstAnalyticsRelationMeta); unsafe impl Send for AnalyticsRelationMeta {} unsafe impl Sync for AnalyticsRelationMeta {} #[derive(Debug, Copy, Clone)] #[doc(alias = "GstAnalyticsRelationMetaInitParams")] pub struct AnalyticsRelationMetaInitParams(ffi::GstAnalyticsRelationMetaInitParams); impl Default for AnalyticsRelationMetaInitParams { fn default() -> Self { Self(ffi::GstAnalyticsRelationMetaInitParams { initial_relation_order: 0, initial_buf_size: 0, }) } } impl AnalyticsRelationMetaInitParams { pub fn new(initial_relation_order: usize, initial_buf_size: usize) -> Self { skip_assert_initialized!(); Self(ffi::GstAnalyticsRelationMetaInitParams { initial_relation_order, initial_buf_size, }) } } #[derive(Debug, Clone)] pub struct AnalyticsMtdRef<'a, T: AnalyticsMtd> { id: u32, meta: gst::MetaRef<'a, AnalyticsRelationMeta>, mtd_type: PhantomData<&'a T>, } #[derive(Debug)] pub struct AnalyticsMtdRefMut<'a, T: AnalyticsMtd> { id: u32, meta: &'a mut gst::MetaRefMut<'a, AnalyticsRelationMeta, gst::meta::Standalone>, mtd_type: PhantomData<&'a T>, } pub struct AnalyticsRelationPath { garray: *mut glib::ffi::GArray, } impl std::fmt::Debug for AnalyticsRelationMeta { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { f.debug_struct("AnalyticsRelationMeta") .field("len", &self.len()) .finish() } } impl AnalyticsRelationMeta { #[doc(alias = "gst_buffer_add_analytics_relation_meta")] pub fn add(buffer: &mut gst::BufferRef) -> gst::MetaRefMut { skip_assert_initialized!(); unsafe { let meta_ptr = ffi::gst_buffer_add_analytics_relation_meta(buffer.as_mut_ptr()); Self::from_mut_ptr(buffer, meta_ptr) } } #[doc(alias = "gst_buffer_add_analytics_relation_meta_full")] pub fn add_full<'a>( buffer: &'a mut gst::BufferRef, init_params: &AnalyticsRelationMetaInitParams, ) -> gst::MetaRefMut<'a, Self, gst::meta::Standalone> { skip_assert_initialized!(); unsafe { let meta_ptr = ffi::gst_buffer_add_analytics_relation_meta_full( buffer.as_mut_ptr(), mut_override(&init_params.0), ); Self::from_mut_ptr(buffer, meta_ptr) } } #[doc(alias = "gst_analytics_relation_get_length")] pub fn len(&self) -> usize { unsafe { ffi::gst_analytics_relation_get_length(self.as_mut_ptr()) } } pub fn is_empty(&self) -> bool { self.len() == 0 } #[doc(alias = "gst_analytics_relation_meta_set_relation")] pub fn set_relation( &mut self, type_: crate::RelTypes, an_meta_first_id: u32, an_meta_second_id: u32, ) -> Result<(), glib::BoolError> { let ret = unsafe { from_glib(ffi::gst_analytics_relation_meta_set_relation( self.as_mut_ptr(), type_.into_glib(), an_meta_first_id, an_meta_second_id, )) }; if ret { Ok(()) } else { Err(glib::bool_error!( "Could not set relation {:}->{:} of type {:?}", an_meta_first_id, an_meta_second_id, type_ )) } } #[doc(alias = "gst_analytics_relation_meta_get_relation")] pub fn relation(&self, an_meta_first_id: u32, an_meta_second_id: u32) -> crate::RelTypes { unsafe { from_glib(ffi::gst_analytics_relation_meta_get_relation( self.as_mut_ptr(), an_meta_first_id, an_meta_second_id, )) } } #[doc(alias = "gst_analytics_relation_meta_exist")] pub fn exist( &self, an_meta_first_id: u32, an_meta_second_id: u32, relation_span: i32, cond_types: crate::RelTypes, ) -> bool { unsafe { from_glib(ffi::gst_analytics_relation_meta_exist( self.as_mut_ptr(), an_meta_first_id, an_meta_second_id, relation_span, cond_types.into_glib(), std::ptr::null_mut(), )) } } #[doc(alias = "gst_analytics_relation_meta_exist")] pub fn exist_path( &self, an_meta_first_id: u32, an_meta_second_id: u32, relation_span: i32, cond_types: crate::RelTypes, ) -> Result { let mut array = std::ptr::null_mut::(); let ret = unsafe { from_glib(ffi::gst_analytics_relation_meta_exist( self.as_mut_ptr(), an_meta_first_id, an_meta_second_id, relation_span, cond_types.into_glib(), &mut array, )) }; if ret { Ok(AnalyticsRelationPath { garray: array }) } else { Err(glib::bool_error!("Such relation doesn't exist")) } } pub unsafe fn as_mut_ptr(&self) -> *mut ffi::GstAnalyticsRelationMeta { mut_override(&self.0) } } impl UnsafeFrom<&AnalyticsRelationMeta> for ffi::GstAnalyticsMtd { unsafe fn unsafe_from(t: &AnalyticsRelationMeta) -> Self { ffi::GstAnalyticsMtd { id: 0, meta: t.as_mut_ptr(), } } } impl AnalyticsRelationPath { pub fn as_slice(&self) -> &[u32] { unsafe { std::slice::from_raw_parts( (*self.garray).data as *const u32, (*self.garray).len as usize, ) } } } impl Drop for AnalyticsRelationPath { fn drop(&mut self) { unsafe { glib::ffi::g_array_free(self.garray, glib::ffi::GTRUE); } } } mod sealed { pub trait Sealed {} impl Sealed for T {} } pub trait AnalyticsMetaRefExt<'a>: sealed::Sealed { #[doc(alias = "gst_analytics_relation_meta_get_mtd")] fn mtd(&self, an_meta_id: u32) -> Option>; fn iter(&'a self) -> AnalyticsMtdIter; fn iter_direct_related( &'a self, an_meta_id: u32, rel_type: RelTypes, ) -> AnalyticsMtdIter; } impl<'a> AnalyticsMetaRefExt<'a> for gst::MetaRef<'a, AnalyticsRelationMeta> { fn mtd(&self, an_meta_id: u32) -> Option> { unsafe { let mut mtd = std::mem::MaybeUninit::uninit(); let ret = from_glib(ffi::gst_analytics_relation_meta_get_mtd( self.as_mut_ptr(), an_meta_id, T::mtd_type(), mtd.as_mut_ptr(), )); let id = mtd.assume_init().id; if ret { Some(AnalyticsMtdRef::from_meta(self, id)) } else { None } } } fn iter(&'a self) -> AnalyticsMtdIter<'a, T> { AnalyticsMtdIter::new(self) } fn iter_direct_related( &'a self, an_meta_id: u32, rel_type: RelTypes, ) -> AnalyticsMtdIter { AnalyticsMtdIter::new_direct_related(self, an_meta_id, rel_type.into_glib()) } } impl<'a, T: AnalyticsMtd> AnalyticsMtdRef<'a, T> { pub fn id(&self) -> u32 { self.id } pub unsafe fn from_meta(meta: &gst::MetaRef<'a, AnalyticsRelationMeta>, id: u32) -> Self { skip_assert_initialized!(); AnalyticsMtdRef { meta: meta.clone(), id, mtd_type: PhantomData, } } #[doc(alias = "gst_analytics_mtd_get_mtd_type")] pub fn mtd_type(&self) -> ffi::GstAnalyticsMtdType { unsafe { let mut mtd = ffi::GstAnalyticsMtd::unsafe_from(self); ffi::gst_analytics_mtd_get_mtd_type(&mut mtd) } } } impl<'a> AnalyticsMtdRef<'a, AnalyticsAnyMtd> { pub fn downcast( self, ) -> Result, AnalyticsMtdRef<'a, AnalyticsAnyMtd>> { if self.mtd_type() == T::mtd_type() { Ok(AnalyticsMtdRef { id: self.id, meta: self.meta, mtd_type: PhantomData, }) } else { Err(self) } } pub fn downcast_ref(&self) -> Option<&AnalyticsMtdRef<'a, T>> { unsafe { if self.mtd_type() == T::mtd_type() { Some(&*(self as *const _ as *const _)) } else { None } } } } impl<'a> AnalyticsMtdRefMut<'a, AnalyticsAnyMtd> { pub fn downcast_mut(&mut self) -> Option<&mut AnalyticsMtdRefMut<'a, T>> { unsafe { if self.as_ref().mtd_type() == T::mtd_type() { Some(&mut *(self as *mut _ as *mut _)) } else { None } } } } impl<'a, T: AnalyticsMtd> UnsafeFrom<&AnalyticsMtdRef<'a, T>> for ffi::GstAnalyticsMtd { unsafe fn unsafe_from(t: &AnalyticsMtdRef<'a, T>) -> Self { ffi::GstAnalyticsMtd { id: t.id, meta: t.meta.as_mut_ptr(), } } } pub trait AnalyticsMetaRefMutExt<'a>: sealed::Sealed { #[doc(alias = "gst_analytics_relation_meta_get_mtd")] fn mtd_mut(&'a mut self, an_meta_id: u32) -> Option>; fn iter_mut(&'a mut self) -> AnalyticsMtdIterMut; fn iter_direct_related_mut( &'a mut self, an_meta_id: u32, rel_type: RelTypes, ) -> AnalyticsMtdIterMut; } impl<'a> AnalyticsMetaRefMutExt<'a> for gst::MetaRefMut<'a, AnalyticsRelationMeta, gst::meta::Standalone> { fn mtd_mut( &'a mut self, an_meta_id: u32, ) -> Option> { unsafe { let mut mtd = std::mem::MaybeUninit::uninit(); let ret = from_glib(ffi::gst_analytics_relation_meta_get_mtd( self.as_mut_ptr(), an_meta_id, T::mtd_type(), mtd.as_mut_ptr(), )); let id = mtd.assume_init().id; if ret { Some(AnalyticsMtdRefMut::from_meta(self, id)) } else { None } } } fn iter_mut(&'a mut self) -> AnalyticsMtdIterMut { AnalyticsMtdIterMut::new(self) } fn iter_direct_related_mut( &'a mut self, an_meta_id: u32, rel_type: RelTypes, ) -> AnalyticsMtdIterMut { AnalyticsMtdIterMut::new_direct_related(self, an_meta_id, rel_type.into_glib()) } } unsafe impl MetaAPI for AnalyticsRelationMeta { type GstType = ffi::GstAnalyticsRelationMeta; #[doc(alias = "gst_analytics_relation_meta_api_get_type")] #[inline] fn meta_api() -> glib::Type { unsafe { from_glib(ffi::gst_analytics_relation_meta_api_get_type()) } } } pub unsafe trait AnalyticsMtd { fn mtd_type() -> ffi::GstAnalyticsMtdType; } pub trait AnalyticsMtdExt: AnalyticsMtd { #[doc(alias = "gst_analytics_mtd_type_get_name")] fn type_name() -> &'static str { unsafe { let ptr = ffi::gst_analytics_mtd_type_get_name(Self::mtd_type()); std::ffi::CStr::from_ptr(ptr).to_str().unwrap() } } } impl AnalyticsMtdExt for T {} impl<'a, T: AnalyticsMtd> AnalyticsMtdRefMut<'a, T> { pub fn id(&self) -> u32 { self.id } pub unsafe fn from_meta( meta: &'a mut gst::MetaRefMut<'a, AnalyticsRelationMeta, gst::meta::Standalone>, id: u32, ) -> Self { skip_assert_initialized!(); AnalyticsMtdRefMut { meta, id, mtd_type: PhantomData, } } } impl<'a, T: AnalyticsMtd> UnsafeFrom<&mut AnalyticsMtdRefMut<'a, T>> for ffi::GstAnalyticsMtd { unsafe fn unsafe_from(t: &mut AnalyticsMtdRefMut<'a, T>) -> Self { ffi::GstAnalyticsMtd { id: t.id, meta: t.meta.as_mut_ptr(), } } } impl<'a, T: AnalyticsMtd> From> for AnalyticsMtdRef<'a, T> { fn from(value: AnalyticsMtdRefMut<'a, T>) -> Self { skip_assert_initialized!(); AnalyticsMtdRef { meta: value.meta.as_ref().clone(), id: value.id, mtd_type: value.mtd_type, } } } impl<'a, T: AnalyticsMtd> From<&mut AnalyticsMtdRefMut<'a, T>> for AnalyticsMtdRef<'a, T> { fn from(value: &mut AnalyticsMtdRefMut<'a, T>) -> Self { skip_assert_initialized!(); AnalyticsMtdRef { meta: value.meta.as_ref().clone(), id: value.id, mtd_type: value.mtd_type, } } } impl<'a, T: AnalyticsMtd> AsRef> for AnalyticsMtdRefMut<'a, T> { #[inline] fn as_ref(&self) -> &AnalyticsMtdRef<'a, T> { unsafe { &*(self as *const AnalyticsMtdRefMut<'a, T> as *const AnalyticsMtdRef<'a, T>) } } } macro_rules! define_mtd_iter { ($name:ident, $metaref:ty, $itemref:ty, $copy_meta:expr) => { pub struct $name<'a, T: AnalyticsMtd> { meta: $metaref, state: glib::ffi::gpointer, mtd_type: ffi::GstAnalyticsMtdType, an_meta_id: u32, rel_type: ffi::GstAnalyticsRelTypes, phantom: std::marker::PhantomData, } impl<'a, T: AnalyticsMtd> $name<'a, T> { fn new(meta: $metaref) -> Self { skip_assert_initialized!(); $name { meta, state: std::ptr::null_mut(), mtd_type: T::mtd_type(), an_meta_id: std::u32::MAX, rel_type: RelTypes::ANY.into_glib(), phantom: PhantomData, } } fn new_direct_related( meta: $metaref, an_meta_id: u32, rel_type: ffi::GstAnalyticsRelTypes, ) -> Self { skip_assert_initialized!(); $name { meta, state: std::ptr::null_mut(), mtd_type: T::mtd_type(), an_meta_id, rel_type, phantom: PhantomData, } } } impl<'a, T: AnalyticsMtd + 'a> Iterator for $name<'a, T> { type Item = $itemref; fn next(&mut self) -> Option { unsafe { let mut mtd = ffi::GstAnalyticsMtd::unsafe_from(&**self.meta); let ret = { if self.an_meta_id == std::u32::MAX { ffi::gst_analytics_relation_meta_iterate( self.meta.as_mut_ptr(), &mut self.state, self.mtd_type, &mut mtd, ) } else { ffi::gst_analytics_relation_meta_get_direct_related( self.meta.as_mut_ptr(), self.an_meta_id, self.rel_type, self.mtd_type, &mut self.state, &mut mtd, ) } }; if from_glib(ret) { // This is a known clippy limitation // https://github.com/rust-lang/rust-clippy/issues/1553 #[allow(clippy::redundant_closure_call)] Some(Self::Item::from_meta($copy_meta(self.meta), mtd.id)) } else { None } } } } }; } define_mtd_iter!( AnalyticsMtdIter, &'a gst::MetaRef<'a, AnalyticsRelationMeta>, AnalyticsMtdRef<'a, T>, |meta| meta ); define_mtd_iter!( AnalyticsMtdIterMut, &'a mut gst::MetaRefMut<'a, AnalyticsRelationMeta, gst::meta::Standalone>, AnalyticsMtdRefMut<'a, T>, |meta: &mut _| &mut *(meta as *mut gst::MetaRefMut< 'a, AnalyticsRelationMeta, gst::meta::Standalone, >) ); #[derive(Debug)] pub enum AnalyticsAnyMtd {} unsafe impl AnalyticsMtd for AnalyticsAnyMtd { fn mtd_type() -> ffi::GstAnalyticsMtdType { ffi::GST_ANALYTICS_MTD_TYPE_ANY as ffi::GstAnalyticsMtdType } } #[cfg(test)] mod tests { use crate::*; #[test] fn build_relation_meta() { gst::init().unwrap(); let mut buf = gst::Buffer::new(); let meta = AnalyticsRelationMeta::add(buf.make_mut()); assert!(meta.is_empty()); } #[test] fn build_relation_meta_full() { gst::init().unwrap(); let mut buf = gst::Buffer::new(); let params = AnalyticsRelationMetaInitParams::new(10, 10); let meta = AnalyticsRelationMeta::add_full(buf.make_mut(), ¶ms); assert!(meta.is_empty()); } #[test] fn relations() { gst::init().unwrap(); let mut buf = gst::Buffer::new(); let _ = AnalyticsRelationMeta::add(buf.make_mut()); let mut meta = buf.make_mut().meta_mut::().unwrap(); let od = meta .add_od_mtd(glib::Quark::from_str("blb"), 0, 1, 10, 20, 0.8) .unwrap(); let od1_id = od.id(); let od = meta .add_od_mtd(glib::Quark::from_str("blb"), 0, 1, 10, 20, 0.8) .unwrap(); let od2_id = od.id(); let od: AnalyticsMtdRef<'_, AnalyticsODMtd> = meta .add_od_mtd(glib::Quark::from_str("blb"), 0, 1, 10, 20, 0.8) .unwrap(); let od3_id = od.id(); meta.set_relation(RelTypes::IS_PART_OF, od1_id, od2_id) .unwrap(); meta.set_relation(RelTypes::IS_PART_OF, od2_id, od3_id) .unwrap(); meta.set_relation(RelTypes::IS_PART_OF, 8888, 9999) .expect_err("Invalid id"); let meta = buf.meta::().unwrap(); assert!(meta.relation(od1_id, od2_id) == crate::RelTypes::IS_PART_OF); assert!(meta.relation(od2_id, od3_id) == crate::RelTypes::IS_PART_OF); assert!(meta.exist(od1_id, od2_id, 1, crate::RelTypes::IS_PART_OF)); assert!(meta.exist(od1_id, od3_id, 2, crate::RelTypes::IS_PART_OF)); assert!(!meta.exist(od2_id, od1_id, 1, crate::RelTypes::IS_PART_OF)); assert!(!meta.exist(od1_id, od3_id, 1, crate::RelTypes::IS_PART_OF)); assert!(!meta.exist(od1_id, od2_id, 1, crate::RelTypes::CONTAIN)); let path = meta .exist_path(od1_id, od3_id, 3, crate::RelTypes::ANY) .unwrap(); assert_eq!(path.as_slice().len(), 3); assert_eq!(path.as_slice()[0], od1_id); assert_eq!(path.as_slice()[1], od2_id); assert_eq!(path.as_slice()[2], od3_id); assert_eq!(meta.len(), meta.iter::().count()); assert_eq!(meta.len(), meta.iter::().count()); for mtd in meta.iter::() { assert_eq!(mtd.obj_type().unwrap(), glib::Quark::from_str("blb")) } assert_eq!(meta.len(), meta.iter::().count()); for mtd in meta.iter::() { if let Ok(mtd) = mtd.downcast::() { assert_eq!(mtd.obj_type().unwrap(), glib::Quark::from_str("blb")) } } assert_eq!( meta.iter_direct_related::(od1_id, crate::RelTypes::IS_PART_OF) .count(), 1 ); assert_eq!( meta.iter_direct_related::(od2_id, crate::RelTypes::IS_PART_OF) .count(), 1 ); assert_eq!( meta.iter_direct_related::(od3_id, crate::RelTypes::IS_PART_OF) .count(), 0 ); assert_eq!( meta.iter_direct_related::(od1_id, crate::RelTypes::CONTAIN) .count(), 0 ); assert_eq!( meta.iter_direct_related::(od1_id, crate::RelTypes::CONTAIN) .count(), 0 ); for mtd in meta.iter_direct_related::(od1_id, crate::RelTypes::IS_PART_OF) { assert_eq!(mtd.obj_type().unwrap(), glib::Quark::from_str("blb")) } let mut meta = buf.make_mut().meta_mut::().unwrap(); assert_eq!(meta.len(), meta.iter_mut::().count()); let mut meta = buf.make_mut().meta_mut::().unwrap(); let _ = meta.add_tracking_mtd(10, gst::ClockTime::from_seconds(10)); let _ = meta.add_tracking_mtd(10, gst::ClockTime::from_seconds(10)); for mut item in meta.iter_mut::() { item.set_lost().unwrap(); } } }