// 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::ptr; use glib::translate::{from_glib, from_glib_none, FromGlib, ToGlib, ToGlibPtr}; use gst::prelude::*; #[repr(transparent)] pub struct VideoMeta(ffi::GstVideoMeta); unsafe impl Send for VideoMeta {} unsafe impl Sync for VideoMeta {} impl VideoMeta { pub fn add( buffer: &mut gst::BufferRef, flags: crate::VideoFrameFlags, format: crate::VideoFormat, width: u32, height: u32, ) -> Result, glib::BoolError> { skip_assert_initialized!(); if format == crate::VideoFormat::Unknown || format == crate::VideoFormat::Encoded { return Err(glib::glib_bool_error!( "Unsupported video format {}", format )); } let info = crate::VideoInfo::builder(format, width, height).build()?; if !info.is_valid() { return Err(glib::glib_bool_error!("Invalid video info")); } if buffer.get_size() < info.size() { return Err(glib::glib_bool_error!( "Buffer smaller than required frame size ({} < {})", buffer.get_size(), info.size() )); } unsafe { let meta = ffi::gst_buffer_add_video_meta( buffer.as_mut_ptr(), flags.to_glib(), format.to_glib(), width, height, ); if meta.is_null() { return Err(glib::glib_bool_error!("Failed to add video meta")); } Ok(Self::from_mut_ptr(buffer, meta)) } } pub fn add_full<'a>( buffer: &'a mut gst::BufferRef, flags: crate::VideoFrameFlags, format: crate::VideoFormat, width: u32, height: u32, offset: &[usize], stride: &[i32], ) -> Result, glib::BoolError> { skip_assert_initialized!(); if format == crate::VideoFormat::Unknown || format == crate::VideoFormat::Encoded { return Err(glib::glib_bool_error!( "Unsupported video format {}", format )); } let n_planes = offset.len() as u32; let info = crate::VideoInfo::builder(format, width, height) .offset(offset) .stride(stride) .build()?; if !info.is_valid() { return Err(glib::glib_bool_error!("Invalid video info")); } if buffer.get_size() < info.size() { return Err(glib::glib_bool_error!( "Buffer smaller than required frame size ({} < {})", buffer.get_size(), info.size() )); } unsafe { let meta = ffi::gst_buffer_add_video_meta_full( buffer.as_mut_ptr(), flags.to_glib(), format.to_glib(), width, height, n_planes, offset.as_ptr() as *mut _, stride.as_ptr() as *mut _, ); if meta.is_null() { return Err(glib::glib_bool_error!("Failed to add video meta")); } Ok(Self::from_mut_ptr(buffer, meta)) } } pub fn get_flags(&self) -> crate::VideoFrameFlags { from_glib(self.0.flags) } pub fn get_format(&self) -> crate::VideoFormat { from_glib(self.0.format) } pub fn get_id(&self) -> i32 { self.0.id } pub fn get_width(&self) -> u32 { self.0.width } pub fn get_height(&self) -> u32 { self.0.height } pub fn get_n_planes(&self) -> u32 { self.0.n_planes } pub fn get_offset(&self) -> &[usize] { &self.0.offset[0..(self.0.n_planes as usize)] } pub fn get_stride(&self) -> &[i32] { &self.0.stride[0..(self.0.n_planes as usize)] } } unsafe impl MetaAPI for VideoMeta { type GstType = ffi::GstVideoMeta; fn get_meta_api() -> glib::Type { unsafe { from_glib(ffi::gst_video_meta_api_get_type()) } } } impl fmt::Debug for VideoMeta { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_struct("VideoMeta") .field("id", &self.get_id()) .field("flags", &self.get_flags()) .field("format", &self.get_format()) .field("width", &self.get_width()) .field("height", &self.get_height()) .field("n_planes", &self.get_n_planes()) .field("offset", &self.get_offset()) .field("stride", &self.get_stride()) .finish() } } #[repr(transparent)] pub struct VideoCropMeta(ffi::GstVideoCropMeta); unsafe impl Send for VideoCropMeta {} unsafe impl Sync for VideoCropMeta {} impl VideoCropMeta { pub fn add( buffer: &mut gst::BufferRef, rect: (u32, u32, u32, u32), ) -> gst::MetaRefMut { skip_assert_initialized!(); unsafe { let meta = gst::ffi::gst_buffer_add_meta( buffer.as_mut_ptr(), ffi::gst_video_crop_meta_get_info(), ptr::null_mut(), ) as *mut ffi::GstVideoCropMeta; { let meta = &mut *meta; meta.x = rect.0; meta.y = rect.1; meta.width = rect.2; meta.height = rect.3; } Self::from_mut_ptr(buffer, meta) } } pub fn get_rect(&self) -> (u32, u32, u32, u32) { (self.0.x, self.0.y, self.0.width, self.0.height) } pub fn set_rect(&mut self, rect: (u32, u32, u32, u32)) { self.0.x = rect.0; self.0.y = rect.1; self.0.width = rect.2; self.0.height = rect.3; } } unsafe impl MetaAPI for VideoCropMeta { type GstType = ffi::GstVideoCropMeta; fn get_meta_api() -> glib::Type { unsafe { from_glib(ffi::gst_video_crop_meta_api_get_type()) } } } impl fmt::Debug for VideoCropMeta { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_struct("VideoCropMeta") .field("rect", &self.get_rect()) .finish() } } #[repr(transparent)] pub struct VideoRegionOfInterestMeta(ffi::GstVideoRegionOfInterestMeta); unsafe impl Send for VideoRegionOfInterestMeta {} unsafe impl Sync for VideoRegionOfInterestMeta {} impl VideoRegionOfInterestMeta { pub fn add<'a>( buffer: &'a mut gst::BufferRef, roi_type: &str, rect: (u32, u32, u32, u32), ) -> gst::MetaRefMut<'a, Self, gst::meta::Standalone> { skip_assert_initialized!(); unsafe { let meta = ffi::gst_buffer_add_video_region_of_interest_meta( buffer.as_mut_ptr(), roi_type.to_glib_none().0, rect.0, rect.1, rect.2, rect.3, ); Self::from_mut_ptr(buffer, meta) } } pub fn get_rect(&self) -> (u32, u32, u32, u32) { (self.0.x, self.0.y, self.0.w, self.0.h) } pub fn get_id(&self) -> i32 { self.0.id } pub fn get_parent_id(&self) -> i32 { self.0.parent_id } pub fn get_roi_type<'a>(&self) -> &'a str { glib::Quark::from_glib(self.0.roi_type).to_string() } #[cfg(feature = "v1_14")] pub fn get_params(&self) -> ParamsIter { ParamsIter { _meta: self, list: self.0.params, } } #[cfg(feature = "v1_14")] pub fn get_param<'b>(&self, name: &'b str) -> Option<&gst::StructureRef> { self.get_params().find(|s| s.get_name() == name) } pub fn set_rect(&mut self, rect: (u32, u32, u32, u32)) { self.0.x = rect.0; self.0.y = rect.1; self.0.w = rect.2; self.0.h = rect.3; } pub fn set_id(&mut self, id: i32) { self.0.id = id } pub fn set_parent_id(&mut self, id: i32) { self.0.parent_id = id } #[cfg(feature = "v1_14")] pub fn add_param(&mut self, s: gst::Structure) { unsafe { ffi::gst_video_region_of_interest_meta_add_param(&mut self.0, s.into_ptr()); } } } #[cfg(feature = "v1_14")] pub struct ParamsIter<'a> { _meta: &'a VideoRegionOfInterestMeta, list: *const glib::ffi::GList, } #[cfg(feature = "v1_14")] impl<'a> Iterator for ParamsIter<'a> { type Item = &'a gst::StructureRef; fn next(&mut self) -> Option<&'a gst::StructureRef> { if self.list.is_null() { return None; } unsafe { let data = (*self.list).data; assert!(!data.is_null()); self.list = (*self.list).next; let s = gst::StructureRef::from_glib_borrow(data as *const gst::ffi::GstStructure); Some(s) } } } unsafe impl MetaAPI for VideoRegionOfInterestMeta { type GstType = ffi::GstVideoRegionOfInterestMeta; fn get_meta_api() -> glib::Type { unsafe { from_glib(ffi::gst_video_region_of_interest_meta_api_get_type()) } } } impl fmt::Debug for VideoRegionOfInterestMeta { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let mut f = f.debug_struct("VideoRegionOfInterestMeta"); f.field("roi_type", &self.get_roi_type()) .field("rect", &self.get_rect()) .field("id", &self.get_id()) .field("parent_id", &self.get_parent_id()); #[cfg(feature = "v1_14")] { f.field("params", &self.get_params().collect::>()); } f.finish() } } #[repr(transparent)] pub struct VideoAffineTransformationMeta(ffi::GstVideoAffineTransformationMeta); unsafe impl Send for VideoAffineTransformationMeta {} unsafe impl Sync for VideoAffineTransformationMeta {} impl VideoAffineTransformationMeta { pub fn add<'a>( buffer: &'a mut gst::BufferRef, matrix: Option<&[f32; 16]>, ) -> gst::MetaRefMut<'a, Self, gst::meta::Standalone> { skip_assert_initialized!(); unsafe { let meta = gst::ffi::gst_buffer_add_meta( buffer.as_mut_ptr(), ffi::gst_video_affine_transformation_meta_get_info(), ptr::null_mut(), ) as *mut ffi::GstVideoAffineTransformationMeta; if let Some(matrix) = matrix { let meta = &mut *meta; meta.matrix.copy_from_slice(matrix); } Self::from_mut_ptr(buffer, meta) } } pub fn get_matrix(&self) -> &[f32; 16] { &self.0.matrix } pub fn set_matrix(&mut self, matrix: &[f32; 16]) { self.0.matrix.copy_from_slice(matrix); } pub fn apply_matrix(&mut self, matrix: &[f32; 16]) { unsafe { ffi::gst_video_affine_transformation_meta_apply_matrix(&mut self.0, matrix); } } } unsafe impl MetaAPI for VideoAffineTransformationMeta { type GstType = ffi::GstVideoAffineTransformationMeta; fn get_meta_api() -> glib::Type { unsafe { from_glib(ffi::gst_video_affine_transformation_meta_api_get_type()) } } } impl fmt::Debug for VideoAffineTransformationMeta { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_struct("VideoAffineTransformationMeta") .field("matrix", &self.get_matrix()) .finish() } } #[repr(transparent)] pub struct VideoOverlayCompositionMeta(ffi::GstVideoOverlayCompositionMeta); unsafe impl Send for VideoOverlayCompositionMeta {} unsafe impl Sync for VideoOverlayCompositionMeta {} impl VideoOverlayCompositionMeta { pub fn add<'a>( buffer: &'a mut gst::BufferRef, overlay: &crate::VideoOverlayComposition, ) -> gst::MetaRefMut<'a, Self, gst::meta::Standalone> { skip_assert_initialized!(); unsafe { let meta = ffi::gst_buffer_add_video_overlay_composition_meta( buffer.as_mut_ptr(), overlay.as_mut_ptr(), ); Self::from_mut_ptr(buffer, meta) } } pub fn get_overlay(&self) -> &crate::VideoOverlayCompositionRef { unsafe { crate::VideoOverlayCompositionRef::from_ptr(self.0.overlay) } } pub fn get_overlay_owned(&self) -> crate::VideoOverlayComposition { unsafe { from_glib_none(self.get_overlay().as_ptr()) } } pub fn set_overlay(&mut self, overlay: &crate::VideoOverlayComposition) { #![allow(clippy::cast_ptr_alignment)] unsafe { gst::ffi::gst_mini_object_unref(self.0.overlay as *mut _); self.0.overlay = gst::ffi::gst_mini_object_ref(overlay.as_mut_ptr() as *mut _) as *mut _; } } } unsafe impl MetaAPI for VideoOverlayCompositionMeta { type GstType = ffi::GstVideoOverlayCompositionMeta; fn get_meta_api() -> glib::Type { unsafe { from_glib(ffi::gst_video_overlay_composition_meta_api_get_type()) } } } impl fmt::Debug for VideoOverlayCompositionMeta { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_struct("VideoOverlayCompositionMeta") .field("overlay", &self.get_overlay()) .finish() } } #[cfg(any(feature = "v1_16", all(not(doctest), doc)))] #[cfg_attr(all(not(doctest), doc), doc(cfg(feature = "v1_16")))] #[repr(transparent)] pub struct VideoCaptionMeta(ffi::GstVideoCaptionMeta); #[cfg(any(feature = "v1_16", all(not(doctest), doc)))] #[cfg_attr(all(not(doctest), doc), doc(cfg(feature = "v1_16")))] unsafe impl Send for VideoCaptionMeta {} #[cfg(any(feature = "v1_16", all(not(doctest), doc)))] #[cfg_attr(all(not(doctest), doc), doc(cfg(feature = "v1_16")))] unsafe impl Sync for VideoCaptionMeta {} #[cfg(any(feature = "v1_16", all(not(doctest), doc)))] #[cfg_attr(all(not(doctest), doc), doc(cfg(feature = "v1_16")))] impl VideoCaptionMeta { pub fn add<'a>( buffer: &'a mut gst::BufferRef, caption_type: crate::VideoCaptionType, data: &[u8], ) -> gst::MetaRefMut<'a, Self, gst::meta::Standalone> { skip_assert_initialized!(); assert!(!data.is_empty()); unsafe { let meta = ffi::gst_buffer_add_video_caption_meta( buffer.as_mut_ptr(), caption_type.to_glib(), data.as_ptr(), data.len(), ); Self::from_mut_ptr(buffer, meta) } } pub fn get_caption_type(&self) -> crate::VideoCaptionType { from_glib(self.0.caption_type) } pub fn get_data(&self) -> &[u8] { unsafe { use std::slice; slice::from_raw_parts(self.0.data, self.0.size) } } } #[cfg(any(feature = "v1_16", all(not(doctest), doc)))] #[cfg_attr(all(not(doctest), doc), doc(cfg(feature = "v1_16")))] unsafe impl MetaAPI for VideoCaptionMeta { type GstType = ffi::GstVideoCaptionMeta; fn get_meta_api() -> glib::Type { unsafe { from_glib(ffi::gst_video_caption_meta_api_get_type()) } } } #[cfg(any(feature = "v1_16", all(not(doctest), doc)))] #[cfg_attr(all(not(doctest), doc), doc(cfg(feature = "v1_16")))] impl fmt::Debug for VideoCaptionMeta { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_struct("VideoCaptionMeta") .field("caption_type", &self.get_caption_type()) .field("data", &self.get_data()) .finish() } } #[cfg(any(feature = "v1_18", all(not(doctest), doc)))] #[cfg_attr(all(not(doctest), doc), doc(cfg(feature = "v1_18")))] #[repr(transparent)] pub struct VideoAFDMeta(ffi::GstVideoAFDMeta); #[cfg(any(feature = "v1_18", all(not(doctest), doc)))] #[cfg_attr(all(not(doctest), doc), doc(cfg(feature = "v1_18")))] unsafe impl Send for VideoAFDMeta {} #[cfg(any(feature = "v1_18", all(not(doctest), doc)))] #[cfg_attr(all(not(doctest), doc), doc(cfg(feature = "v1_18")))] unsafe impl Sync for VideoAFDMeta {} #[cfg(any(feature = "v1_18", all(not(doctest), doc)))] #[cfg_attr(all(not(doctest), doc), doc(cfg(feature = "v1_18")))] impl VideoAFDMeta { pub fn add( buffer: &mut gst::BufferRef, field: u8, spec: crate::VideoAFDSpec, afd: crate::VideoAFDValue, ) -> gst::MetaRefMut { skip_assert_initialized!(); unsafe { let meta = ffi::gst_buffer_add_video_afd_meta( buffer.as_mut_ptr(), field, spec.to_glib(), afd.to_glib(), ); Self::from_mut_ptr(buffer, meta) } } pub fn get_field(&self) -> u8 { self.0.field } pub fn get_spec(&self) -> crate::VideoAFDSpec { from_glib(self.0.spec) } pub fn get_afd(&self) -> crate::VideoAFDValue { from_glib(self.0.afd) } } #[cfg(any(feature = "v1_18", all(not(doctest), doc)))] #[cfg_attr(all(not(doctest), doc), doc(cfg(feature = "v1_18")))] unsafe impl MetaAPI for VideoAFDMeta { type GstType = ffi::GstVideoAFDMeta; fn get_meta_api() -> glib::Type { unsafe { from_glib(ffi::gst_video_afd_meta_api_get_type()) } } } #[cfg(any(feature = "v1_18", all(not(doctest), doc)))] #[cfg_attr(all(not(doctest), doc), doc(cfg(feature = "v1_18")))] impl fmt::Debug for VideoAFDMeta { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_struct("VideoAFDMeta") .field("field", &self.get_field()) .field("spec", &self.get_spec()) .field("afd", &self.get_afd()) .finish() } } #[cfg(any(feature = "v1_18", all(not(doctest), doc)))] #[cfg_attr(all(not(doctest), doc), doc(cfg(feature = "v1_18")))] #[repr(transparent)] pub struct VideoBarMeta(ffi::GstVideoBarMeta); #[cfg(any(feature = "v1_18", all(not(doctest), doc)))] #[cfg_attr(all(not(doctest), doc), doc(cfg(feature = "v1_18")))] unsafe impl Send for VideoBarMeta {} #[cfg(any(feature = "v1_18", all(not(doctest), doc)))] #[cfg_attr(all(not(doctest), doc), doc(cfg(feature = "v1_18")))] unsafe impl Sync for VideoBarMeta {} #[cfg(any(feature = "v1_18", all(not(doctest), doc)))] #[cfg_attr(all(not(doctest), doc), doc(cfg(feature = "v1_18")))] impl VideoBarMeta { pub fn add( buffer: &mut gst::BufferRef, field: u8, is_letterbox: bool, bar_data1: u32, bar_data2: u32, ) -> gst::MetaRefMut { skip_assert_initialized!(); unsafe { let meta = ffi::gst_buffer_add_video_bar_meta( buffer.as_mut_ptr(), field, is_letterbox.to_glib(), bar_data1, bar_data2, ); Self::from_mut_ptr(buffer, meta) } } pub fn get_field(&self) -> u8 { self.0.field } pub fn is_letterbox(&self) -> bool { from_glib(self.0.is_letterbox) } pub fn get_bar_data1(&self) -> u32 { self.0.bar_data1 } pub fn get_bar_data2(&self) -> u32 { self.0.bar_data2 } } #[cfg(any(feature = "v1_18", all(not(doctest), doc)))] #[cfg_attr(all(not(doctest), doc), doc(cfg(feature = "v1_18")))] unsafe impl MetaAPI for VideoBarMeta { type GstType = ffi::GstVideoBarMeta; fn get_meta_api() -> glib::Type { unsafe { from_glib(ffi::gst_video_bar_meta_api_get_type()) } } } #[cfg(any(feature = "v1_18", all(not(doctest), doc)))] #[cfg_attr(all(not(doctest), doc), doc(cfg(feature = "v1_18")))] impl fmt::Debug for VideoBarMeta { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_struct("VideoBarMeta") .field("field", &self.get_field()) .field("is_letterbox", &self.is_letterbox()) .field("bar_data1", &self.get_bar_data1()) .field("bar_data2", &self.get_bar_data2()) .finish() } } #[cfg(test)] mod tests { use super::*; #[test] fn test_add_get_meta() { gst::init().unwrap(); let mut buffer = gst::Buffer::with_size(320 * 240 * 4).unwrap(); { let meta = VideoMeta::add( buffer.get_mut().unwrap(), crate::VideoFrameFlags::empty(), crate::VideoFormat::Argb, 320, 240, ) .unwrap(); assert_eq!(meta.get_id(), 0); assert_eq!(meta.get_flags(), crate::VideoFrameFlags::empty()); assert_eq!(meta.get_format(), crate::VideoFormat::Argb); assert_eq!(meta.get_width(), 320); assert_eq!(meta.get_height(), 240); assert_eq!(meta.get_n_planes(), 1); assert_eq!(meta.get_offset(), &[0]); assert_eq!(meta.get_stride(), &[320 * 4]); } { let meta = buffer.get_meta::().unwrap(); assert_eq!(meta.get_id(), 0); assert_eq!(meta.get_flags(), crate::VideoFrameFlags::empty()); assert_eq!(meta.get_format(), crate::VideoFormat::Argb); assert_eq!(meta.get_width(), 320); assert_eq!(meta.get_height(), 240); assert_eq!(meta.get_n_planes(), 1); assert_eq!(meta.get_offset(), &[0]); assert_eq!(meta.get_stride(), &[320 * 4]); } } #[test] fn test_add_full_get_meta() { gst::init().unwrap(); let mut buffer = gst::Buffer::with_size(320 * 240 * 4).unwrap(); { let meta = VideoMeta::add_full( buffer.get_mut().unwrap(), crate::VideoFrameFlags::empty(), crate::VideoFormat::Argb, 320, 240, &[0], &[320 * 4], ) .unwrap(); assert_eq!(meta.get_id(), 0); assert_eq!(meta.get_flags(), crate::VideoFrameFlags::empty()); assert_eq!(meta.get_format(), crate::VideoFormat::Argb); assert_eq!(meta.get_width(), 320); assert_eq!(meta.get_height(), 240); assert_eq!(meta.get_n_planes(), 1); assert_eq!(meta.get_offset(), &[0]); assert_eq!(meta.get_stride(), &[320 * 4]); } { let meta = buffer.get_meta::().unwrap(); assert_eq!(meta.get_id(), 0); assert_eq!(meta.get_flags(), crate::VideoFrameFlags::empty()); assert_eq!(meta.get_format(), crate::VideoFormat::Argb); assert_eq!(meta.get_width(), 320); assert_eq!(meta.get_height(), 240); assert_eq!(meta.get_n_planes(), 1); assert_eq!(meta.get_offset(), &[0]); assert_eq!(meta.get_stride(), &[320 * 4]); } } }