diff --git a/gstreamer-video/src/subclass/mod.rs b/gstreamer-video/src/subclass/mod.rs index 484484746..3a374f239 100644 --- a/gstreamer-video/src/subclass/mod.rs +++ b/gstreamer-video/src/subclass/mod.rs @@ -3,6 +3,12 @@ #![allow(clippy::cast_ptr_alignment)] mod navigation; +#[cfg(any(feature = "v1_16", feature = "dox"))] +#[cfg_attr(feature = "dox", doc(cfg(feature = "v1_16")))] +mod video_aggregator; +#[cfg(any(feature = "v1_16", feature = "dox"))] +#[cfg_attr(feature = "dox", doc(cfg(feature = "v1_16")))] +mod video_aggregator_pad; mod video_decoder; mod video_encoder; mod video_filter; @@ -17,6 +23,12 @@ pub mod prelude { pub use gst_base::subclass::prelude::*; pub use super::navigation::NavigationImpl; + #[cfg(any(feature = "v1_16", feature = "dox"))] + #[cfg_attr(feature = "dox", doc(cfg(feature = "v1_16")))] + pub use super::video_aggregator::{VideoAggregatorImpl, VideoAggregatorImplExt}; + #[cfg(any(feature = "v1_16", feature = "dox"))] + #[cfg_attr(feature = "dox", doc(cfg(feature = "v1_16")))] + pub use super::video_aggregator_pad::{VideoAggregatorPadImpl, VideoAggregatorPadImplExt}; pub use super::video_decoder::{VideoDecoderImpl, VideoDecoderImplExt}; pub use super::video_encoder::{VideoEncoderImpl, VideoEncoderImplExt}; pub use super::video_filter::{VideoFilterImpl, VideoFilterImplExt}; diff --git a/gstreamer-video/src/subclass/video_aggregator.rs b/gstreamer-video/src/subclass/video_aggregator.rs new file mode 100644 index 000000000..2e09871a7 --- /dev/null +++ b/gstreamer-video/src/subclass/video_aggregator.rs @@ -0,0 +1,289 @@ +// Take a look at the license at the top of the repository in the LICENSE file. + +use glib::translate::*; + +use gst_base::prelude::*; +use gst_base::subclass::prelude::*; + +use std::mem; +use std::ptr; + +use crate::VideoAggregator; + +pub struct AggregateFramesToken<'a>(pub(crate) &'a VideoAggregator); + +pub trait VideoAggregatorImpl: VideoAggregatorImplExt + AggregatorImpl { + fn update_caps( + &self, + element: &Self::Type, + caps: &gst::Caps, + ) -> Result { + self.parent_update_caps(element, caps) + } + + fn aggregate_frames( + &self, + element: &Self::Type, + token: &AggregateFramesToken, + outbuf: &mut gst::BufferRef, + ) -> Result { + self.parent_aggregate_frames(element, token, outbuf) + } + + fn create_output_buffer( + &self, + element: &Self::Type, + ) -> Result, gst::FlowError> { + self.parent_create_output_buffer(element) + } + + fn find_best_format( + &self, + element: &Self::Type, + downstream_caps: &gst::Caps, + ) -> Option<(crate::VideoInfo, bool)> { + self.parent_find_best_format(element, downstream_caps) + } +} + +pub trait VideoAggregatorImplExt: ObjectSubclass { + fn parent_update_caps( + &self, + element: &Self::Type, + caps: &gst::Caps, + ) -> Result; + + fn parent_aggregate_frames( + &self, + element: &Self::Type, + token: &AggregateFramesToken, + outbuf: &mut gst::BufferRef, + ) -> Result; + + fn parent_create_output_buffer( + &self, + element: &Self::Type, + ) -> Result, gst::FlowError>; + + fn parent_find_best_format( + &self, + element: &Self::Type, + downstream_caps: &gst::Caps, + ) -> Option<(crate::VideoInfo, bool)>; +} + +impl VideoAggregatorImplExt for T { + fn parent_update_caps( + &self, + element: &Self::Type, + caps: &gst::Caps, + ) -> Result { + unsafe { + let data = Self::type_data(); + let parent_class = data.as_ref().parent_class() as *mut ffi::GstVideoAggregatorClass; + let f = (*parent_class) + .update_caps + .expect("Missing parent function `update_caps`"); + + Option::<_>::from_glib_full(f( + element + .unsafe_cast_ref::() + .to_glib_none() + .0, + caps.as_mut_ptr(), + )) + .ok_or_else(|| { + gst::loggable_error!(gst::CAT_RUST, "Parent function `update_caps` failed") + }) + } + } + + fn parent_aggregate_frames( + &self, + element: &Self::Type, + token: &AggregateFramesToken, + outbuf: &mut gst::BufferRef, + ) -> Result { + assert_eq!( + element.as_ptr() as *mut ffi::GstVideoAggregator, + token.0.as_ptr() as *mut ffi::GstVideoAggregator + ); + + unsafe { + let data = Self::type_data(); + let parent_class = data.as_ref().parent_class() as *mut ffi::GstVideoAggregatorClass; + let f = (*parent_class) + .aggregate_frames + .expect("Missing parent function `aggregate_frames`"); + + try_from_glib(f( + element + .unsafe_cast_ref::() + .to_glib_none() + .0, + // FIXME: Wrong pointer type + outbuf.as_mut_ptr() as *mut *mut gst::ffi::GstBuffer, + )) + } + } + + fn parent_create_output_buffer( + &self, + element: &Self::Type, + ) -> Result, gst::FlowError> { + unsafe { + let data = Self::type_data(); + let parent_class = data.as_ref().parent_class() as *mut ffi::GstVideoAggregatorClass; + let f = (*parent_class) + .create_output_buffer + .expect("Missing parent function `create_output_buffer`"); + + let mut buffer = ptr::null_mut(); + try_from_glib(f( + element + .unsafe_cast_ref::() + .to_glib_none() + .0, + &mut buffer, + )) + .map(|_: gst::FlowSuccess| from_glib_full(buffer)) + } + } + + fn parent_find_best_format( + &self, + element: &Self::Type, + downstream_caps: &gst::Caps, + ) -> Option<(crate::VideoInfo, bool)> { + unsafe { + let data = Self::type_data(); + let parent_class = data.as_ref().parent_class() as *mut ffi::GstVideoAggregatorClass; + (*parent_class).find_best_format.and_then(|f| { + let mut info = mem::MaybeUninit::zeroed(); + ffi::gst_video_info_init(info.as_mut_ptr()); + let mut info = info.assume_init(); + + let mut at_least_one_alpha = glib::ffi::GFALSE; + + f( + element + .unsafe_cast_ref::() + .to_glib_none() + .0, + downstream_caps.as_mut_ptr(), + &mut info, + &mut at_least_one_alpha, + ); + + if info.finfo.is_null() { + None + } else { + Some(( + from_glib_none(mut_override(&info as *const ffi::GstVideoInfo)), + from_glib(at_least_one_alpha), + )) + } + }) + } + } +} + +unsafe impl IsSubclassable for VideoAggregator { + fn class_init(klass: &mut glib::Class) { + Self::parent_class_init::(klass); + + let klass = klass.as_mut(); + klass.update_caps = Some(video_aggregator_update_caps::); + klass.aggregate_frames = Some(video_aggregator_aggregate_frames::); + klass.create_output_buffer = Some(video_aggregator_create_output_buffer::); + klass.find_best_format = Some(video_aggregator_find_best_format::); + } +} + +unsafe extern "C" fn video_aggregator_update_caps( + ptr: *mut ffi::GstVideoAggregator, + caps: *mut gst::ffi::GstCaps, +) -> *mut gst::ffi::GstCaps { + let instance = &*(ptr as *mut T::Instance); + let imp = instance.imp(); + let wrap: Borrowed = from_glib_borrow(ptr); + + gst::panic_to_error!(&wrap, imp.panicked(), ptr::null_mut(), { + match imp.update_caps(wrap.unsafe_cast_ref(), &from_glib_borrow(caps)) { + Ok(caps) => caps.into_ptr(), + Err(err) => { + err.log_with_object(&*wrap); + ptr::null_mut() + } + } + }) +} + +unsafe extern "C" fn video_aggregator_aggregate_frames( + ptr: *mut ffi::GstVideoAggregator, + outbuf: *mut *mut gst::ffi::GstBuffer, +) -> gst::ffi::GstFlowReturn { + let instance = &*(ptr as *mut T::Instance); + let imp = instance.imp(); + let wrap: Borrowed = from_glib_borrow(ptr); + + gst::panic_to_error!(&wrap, imp.panicked(), gst::FlowReturn::Error, { + let token = AggregateFramesToken(&*wrap); + + imp.aggregate_frames( + wrap.unsafe_cast_ref(), + &token, + gst::BufferRef::from_mut_ptr( + // Wrong pointer type + outbuf as *mut gst::ffi::GstBuffer, + ), + ) + .into() + }) + .into_glib() +} + +unsafe extern "C" fn video_aggregator_create_output_buffer( + ptr: *mut ffi::GstVideoAggregator, + outbuf: *mut *mut gst::ffi::GstBuffer, +) -> gst::ffi::GstFlowReturn { + let instance = &*(ptr as *mut T::Instance); + let imp = instance.imp(); + let wrap: Borrowed = from_glib_borrow(ptr); + + gst::panic_to_error!(&wrap, imp.panicked(), gst::FlowReturn::Error, { + match imp.create_output_buffer(wrap.unsafe_cast_ref()) { + Ok(buffer) => { + *outbuf = buffer.map(|b| b.into_ptr()).unwrap_or(ptr::null_mut()); + Ok(gst::FlowSuccess::Ok) + } + Err(err) => { + *outbuf = ptr::null_mut(); + Err(err) + } + } + .into() + }) + .into_glib() +} + +unsafe extern "C" fn video_aggregator_find_best_format( + ptr: *mut ffi::GstVideoAggregator, + downstream_caps: *mut gst::ffi::GstCaps, + best_info: *mut ffi::GstVideoInfo, + at_least_one_alpha: *mut glib::ffi::gboolean, +) { + let instance = &*(ptr as *mut T::Instance); + let imp = instance.imp(); + let wrap: Borrowed = from_glib_borrow(ptr); + + gst::panic_to_error!(&wrap, imp.panicked(), (), { + match imp.find_best_format(wrap.unsafe_cast_ref(), &from_glib_borrow(downstream_caps)) { + None => (), + Some((info, alpha)) => { + *best_info = *info.to_glib_none().0; + *at_least_one_alpha = alpha.into_glib(); + } + } + }) +} diff --git a/gstreamer-video/src/subclass/video_aggregator_pad.rs b/gstreamer-video/src/subclass/video_aggregator_pad.rs new file mode 100644 index 000000000..7b75c06d0 --- /dev/null +++ b/gstreamer-video/src/subclass/video_aggregator_pad.rs @@ -0,0 +1,213 @@ +// Take a look at the license at the top of the repository in the LICENSE file. + +use glib::translate::*; + +use gst_base::prelude::*; +use gst_base::subclass::prelude::*; + +use std::mem; +use std::ptr; + +use crate::subclass::AggregateFramesToken; +use crate::VideoAggregator; +use crate::VideoAggregatorPad; + +pub trait VideoAggregatorPadImpl: VideoAggregatorPadImplExt + AggregatorPadImpl { + fn update_conversion_info(&self, pad: &Self::Type) { + self.parent_update_conversion_info(pad) + } + + fn prepare_frame( + &self, + pad: &Self::Type, + aggregator: &crate::VideoAggregator, + token: &AggregateFramesToken, + buffer: &gst::Buffer, + ) -> Option> { + self.parent_prepare_frame(pad, aggregator, token, buffer) + } + + fn clean_frame( + &self, + pad: &Self::Type, + aggregator: &crate::VideoAggregator, + token: &AggregateFramesToken, + frame: Option>, + ) { + self.parent_clean_frame(pad, aggregator, token, frame) + } +} + +pub trait VideoAggregatorPadImplExt: ObjectSubclass { + fn parent_update_conversion_info(&self, pad: &Self::Type); + + fn parent_prepare_frame( + &self, + pad: &Self::Type, + aggregator: &crate::VideoAggregator, + token: &AggregateFramesToken, + buffer: &gst::Buffer, + ) -> Option>; + + fn parent_clean_frame( + &self, + pad: &Self::Type, + aggregator: &crate::VideoAggregator, + token: &AggregateFramesToken, + frame: Option>, + ); +} + +impl VideoAggregatorPadImplExt for T { + fn parent_update_conversion_info(&self, pad: &Self::Type) { + unsafe { + let data = Self::type_data(); + let parent_class = data.as_ref().parent_class() as *mut ffi::GstVideoAggregatorPadClass; + if let Some(f) = (*parent_class).update_conversion_info { + f(pad.unsafe_cast_ref::().to_glib_none().0); + } + } + } + + fn parent_prepare_frame( + &self, + pad: &Self::Type, + aggregator: &crate::VideoAggregator, + token: &AggregateFramesToken, + buffer: &gst::Buffer, + ) -> Option> { + assert_eq!( + aggregator.as_ptr() as *mut ffi::GstVideoAggregator, + token.0.as_ptr() as *mut ffi::GstVideoAggregator + ); + + unsafe { + let data = Self::type_data(); + let parent_class = data.as_ref().parent_class() as *mut ffi::GstVideoAggregatorPadClass; + if let Some(f) = (*parent_class).prepare_frame { + let mut prepared_frame = mem::MaybeUninit::zeroed(); + + f( + pad.unsafe_cast_ref::().to_glib_none().0, + aggregator.to_glib_none().0, + buffer.as_mut_ptr(), + prepared_frame.as_mut_ptr(), + ); + + let prepared_frame = prepared_frame.assume_init(); + if prepared_frame.buffer.is_null() { + None + } else { + Some(crate::VideoFrame::from_glib_full(prepared_frame)) + } + } else { + None + } + } + } + + fn parent_clean_frame( + &self, + pad: &Self::Type, + aggregator: &crate::VideoAggregator, + token: &AggregateFramesToken, + frame: Option>, + ) { + assert_eq!( + aggregator.as_ptr() as *mut ffi::GstVideoAggregator, + token.0.as_ptr() as *mut ffi::GstVideoAggregator + ); + + unsafe { + let data = Self::type_data(); + let parent_class = data.as_ref().parent_class() as *mut ffi::GstVideoAggregatorPadClass; + if let Some(f) = (*parent_class).clean_frame { + let mut prepared_frame = if let Some(frame) = frame { + frame.into_raw() + } else { + mem::zeroed() + }; + + f( + pad.unsafe_cast_ref::().to_glib_none().0, + aggregator.to_glib_none().0, + &mut prepared_frame, + ); + } + } + } +} + +unsafe impl IsSubclassable for VideoAggregatorPad { + fn class_init(klass: &mut glib::Class) { + Self::parent_class_init::(klass); + + let klass = klass.as_mut(); + klass.update_conversion_info = Some(video_aggregator_pad_update_conversion_info::); + klass.prepare_frame = Some(video_aggregator_pad_prepare_frame::); + klass.clean_frame = Some(video_aggregator_pad_clean_frame::); + } +} + +unsafe extern "C" fn video_aggregator_pad_update_conversion_info( + ptr: *mut ffi::GstVideoAggregatorPad, +) { + let instance = &*(ptr as *mut T::Instance); + let imp = instance.imp(); + let wrap: Borrowed = from_glib_borrow(ptr); + + imp.update_conversion_info(wrap.unsafe_cast_ref()); +} + +unsafe extern "C" fn video_aggregator_pad_prepare_frame( + ptr: *mut ffi::GstVideoAggregatorPad, + aggregator: *mut ffi::GstVideoAggregator, + buffer: *mut gst::ffi::GstBuffer, + prepared_frame: *mut ffi::GstVideoFrame, +) -> glib::ffi::gboolean { + let instance = &*(ptr as *mut T::Instance); + let imp = instance.imp(); + let wrap: Borrowed = from_glib_borrow(ptr); + let aggregator: Borrowed = from_glib_borrow(aggregator); + + let token = AggregateFramesToken(&*aggregator); + + match imp.prepare_frame( + wrap.unsafe_cast_ref(), + &aggregator, + &token, + &from_glib_borrow(buffer), + ) { + Some(frame) => { + *prepared_frame = frame.into_raw(); + } + None => { + ptr::write(prepared_frame, mem::zeroed()); + } + } + + glib::ffi::GTRUE +} + +unsafe extern "C" fn video_aggregator_pad_clean_frame( + ptr: *mut ffi::GstVideoAggregatorPad, + aggregator: *mut ffi::GstVideoAggregator, + prepared_frame: *mut ffi::GstVideoFrame, +) { + let instance = &*(ptr as *mut T::Instance); + let imp = instance.imp(); + let wrap: Borrowed = from_glib_borrow(ptr); + let aggregator: Borrowed = from_glib_borrow(aggregator); + + let token = AggregateFramesToken(&*aggregator); + + let frame = if (*prepared_frame).buffer.is_null() { + None + } else { + let frame = crate::VideoFrame::from_glib_full(*prepared_frame); + ptr::write(prepared_frame, mem::zeroed()); + Some(frame) + }; + + imp.clean_frame(wrap.unsafe_cast_ref(), &aggregator, &token, frame); +}