// Take a look at the license at the top of the repository in the LICENSE file. use std::ptr; use glib::{prelude::*, subclass::prelude::*, translate::*}; use crate::{RTSPMedia, RTSPThread}; #[derive(Debug)] pub struct SDPInfo(ptr::NonNull); impl SDPInfo { pub fn is_ipv6(&self) -> bool { unsafe { from_glib(self.0.as_ref().is_ipv6) } } pub fn server_ip(&self) -> &str { unsafe { use std::ffi::CStr; CStr::from_ptr(self.0.as_ref().server_ip).to_str().unwrap() } } } pub trait RTSPMediaImpl: RTSPMediaImplExt + ObjectImpl + Send + Sync { fn handle_message(&self, message: &gst::MessageRef) -> bool { self.parent_handle_message(message) } fn prepare(&self, thread: &RTSPThread) -> Result<(), gst::LoggableError> { self.parent_prepare(thread) } fn unprepare(&self) -> Result<(), gst::LoggableError> { self.parent_unprepare() } fn suspend(&self) -> Result<(), gst::LoggableError> { self.parent_suspend() } fn unsuspend(&self) -> Result<(), gst::LoggableError> { self.parent_unsuspend() } // TODO missing: convert_range fn query_position(&self) -> Option { self.parent_query_position() } fn query_stop(&self) -> Option { self.parent_query_stop() } fn create_rtpbin(&self) -> Option { self.parent_create_rtpbin() } fn setup_rtpbin(&self, rtpbin: &gst::Element) -> Result<(), gst::LoggableError> { self.parent_setup_rtpbin(rtpbin) } fn setup_sdp( &self, sdp: &mut gst_sdp::SDPMessageRef, info: &SDPInfo, ) -> Result<(), gst::LoggableError> { self.parent_setup_sdp(sdp, info) } fn new_stream(&self, stream: &crate::RTSPStream) { self.parent_new_stream(stream); } fn removed_stream(&self, stream: &crate::RTSPStream) { self.parent_removed_stream(stream); } fn prepared(&self) { self.parent_prepared(); } fn unprepared(&self) { self.parent_unprepared(); } fn target_state(&self, state: gst::State) { self.parent_target_state(state); } fn new_state(&self, state: gst::State) { self.parent_new_state(state); } fn handle_sdp(&self, sdp: &gst_sdp::SDPMessageRef) -> Result<(), gst::LoggableError> { self.parent_handle_sdp(sdp) } } mod sealed { pub trait Sealed {} impl Sealed for T {} } pub trait RTSPMediaImplExt: sealed::Sealed + ObjectSubclass { fn parent_handle_message(&self, message: &gst::MessageRef) -> bool { unsafe { let data = Self::type_data(); let parent_class = data.as_ref().parent_class() as *mut ffi::GstRTSPMediaClass; if let Some(f) = (*parent_class).handle_message { from_glib(f( self.obj().unsafe_cast_ref::().to_glib_none().0, message.as_ptr() as *mut _, )) } else { false } } } fn parent_prepare(&self, thread: &RTSPThread) -> Result<(), gst::LoggableError> { unsafe { let data = Self::type_data(); let parent_class = data.as_ref().parent_class() as *mut ffi::GstRTSPMediaClass; if let Some(f) = (*parent_class).prepare { gst::result_from_gboolean!( f( self.obj().unsafe_cast_ref::().to_glib_none().0, thread.to_glib_none().0 ), gst::CAT_RUST, "Parent function `prepare` failed" ) } else { Ok(()) } } } fn parent_unprepare(&self) -> Result<(), gst::LoggableError> { unsafe { let data = Self::type_data(); let parent_class = data.as_ref().parent_class() as *mut ffi::GstRTSPMediaClass; if let Some(f) = (*parent_class).unprepare { gst::result_from_gboolean!( f(self.obj().unsafe_cast_ref::().to_glib_none().0), gst::CAT_RUST, "Parent function `unprepare` failed" ) } else { Ok(()) } } } fn parent_suspend(&self) -> Result<(), gst::LoggableError> { unsafe { let data = Self::type_data(); let parent_class = data.as_ref().parent_class() as *mut ffi::GstRTSPMediaClass; if let Some(f) = (*parent_class).suspend { gst::result_from_gboolean!( f(self.obj().unsafe_cast_ref::().to_glib_none().0), gst::CAT_RUST, "Parent function `suspend` failed" ) } else { Ok(()) } } } fn parent_unsuspend(&self) -> Result<(), gst::LoggableError> { unsafe { let data = Self::type_data(); let parent_class = data.as_ref().parent_class() as *mut ffi::GstRTSPMediaClass; if let Some(f) = (*parent_class).unsuspend { gst::result_from_gboolean!( f(self.obj().unsafe_cast_ref::().to_glib_none().0), gst::CAT_RUST, "Parent function `unsuspend` failed" ) } else { Ok(()) } } } // TODO missing: convert_range fn parent_query_position(&self) -> Option { unsafe { use std::mem; let data = Self::type_data(); let parent_class = data.as_ref().parent_class() as *mut ffi::GstRTSPMediaClass; if let Some(f) = (*parent_class).query_position { let mut position = mem::MaybeUninit::uninit(); if f( self.obj().unsafe_cast_ref::().to_glib_none().0, position.as_mut_ptr(), ) == glib::ffi::GFALSE { None } else { from_glib(position.assume_init() as u64) } } else { None } } } fn parent_query_stop(&self) -> Option { unsafe { use std::mem; let data = Self::type_data(); let parent_class = data.as_ref().parent_class() as *mut ffi::GstRTSPMediaClass; if let Some(f) = (*parent_class).query_stop { let mut stop = mem::MaybeUninit::uninit(); if f( self.obj().unsafe_cast_ref::().to_glib_none().0, stop.as_mut_ptr(), ) == glib::ffi::GFALSE { None } else { from_glib(stop.assume_init() as u64) } } else { None } } } fn parent_create_rtpbin(&self) -> Option { unsafe { let data = Self::type_data(); let parent_class = data.as_ref().parent_class() as *mut ffi::GstRTSPMediaClass; let f = (*parent_class) .create_rtpbin .expect("No `create_rtpbin` virtual method implementation in parent class"); from_glib_none(f(self .obj() .unsafe_cast_ref::() .to_glib_none() .0)) } } fn parent_setup_rtpbin(&self, rtpbin: &gst::Element) -> Result<(), gst::LoggableError> { unsafe { let data = Self::type_data(); let parent_class = data.as_ref().parent_class() as *mut ffi::GstRTSPMediaClass; if let Some(f) = (*parent_class).setup_rtpbin { let ptr = rtpbin.to_glib_none().0; // The C code assumes to pass a floating reference around so let's make sure we do glib::gobject_ffi::g_object_force_floating(ptr as *mut _); let res = gst::result_from_gboolean!( f( self.obj().unsafe_cast_ref::().to_glib_none().0, ptr ), gst::CAT_RUST, "Parent function `setup_sdp` failed" ); // If the code didn't accidentally sink it then we have to do that // here now so that we don't have any floating reference on our side // anymore if glib::gobject_ffi::g_object_is_floating(ptr as *mut _) != glib::ffi::GFALSE { glib::gobject_ffi::g_object_ref_sink(ptr as *mut _); } res } else { Ok(()) } } } fn parent_setup_sdp( &self, sdp: &mut gst_sdp::SDPMessageRef, info: &SDPInfo, ) -> Result<(), gst::LoggableError> { unsafe { let data = Self::type_data(); let parent_class = data.as_ref().parent_class() as *mut ffi::GstRTSPMediaClass; let f = (*parent_class) .setup_sdp .expect("No `setup_sdp` virtual method implementation in parent class"); gst::result_from_gboolean!( f( self.obj().unsafe_cast_ref::().to_glib_none().0, sdp as *mut _ as *mut gst_sdp::ffi::GstSDPMessage, info.0.as_ptr() ), gst::CAT_RUST, "Parent function `setup_sdp` failed" ) } } fn parent_new_stream(&self, stream: &crate::RTSPStream) { unsafe { let data = Self::type_data(); let parent_class = data.as_ref().parent_class() as *mut ffi::GstRTSPMediaClass; if let Some(f) = (*parent_class).new_stream { f( self.obj().unsafe_cast_ref::().to_glib_none().0, stream.to_glib_none().0, ); } } } fn parent_removed_stream(&self, stream: &crate::RTSPStream) { unsafe { let data = Self::type_data(); let parent_class = data.as_ref().parent_class() as *mut ffi::GstRTSPMediaClass; if let Some(f) = (*parent_class).removed_stream { f( self.obj().unsafe_cast_ref::().to_glib_none().0, stream.to_glib_none().0, ); } } } fn parent_prepared(&self) { unsafe { let data = Self::type_data(); let parent_class = data.as_ref().parent_class() as *mut ffi::GstRTSPMediaClass; if let Some(f) = (*parent_class).prepared { f(self.obj().unsafe_cast_ref::().to_glib_none().0); } } } fn parent_unprepared(&self) { unsafe { let data = Self::type_data(); let parent_class = data.as_ref().parent_class() as *mut ffi::GstRTSPMediaClass; if let Some(f) = (*parent_class).unprepared { f(self.obj().unsafe_cast_ref::().to_glib_none().0); } } } fn parent_target_state(&self, state: gst::State) { unsafe { let data = Self::type_data(); let parent_class = data.as_ref().parent_class() as *mut ffi::GstRTSPMediaClass; if let Some(f) = (*parent_class).target_state { f( self.obj().unsafe_cast_ref::().to_glib_none().0, state.into_glib(), ); } } } fn parent_new_state(&self, state: gst::State) { unsafe { let data = Self::type_data(); let parent_class = data.as_ref().parent_class() as *mut ffi::GstRTSPMediaClass; if let Some(f) = (*parent_class).new_state { f( self.obj().unsafe_cast_ref::().to_glib_none().0, state.into_glib(), ); } } } fn parent_handle_sdp(&self, sdp: &gst_sdp::SDPMessageRef) -> Result<(), gst::LoggableError> { unsafe { let data = Self::type_data(); let parent_class = data.as_ref().parent_class() as *mut ffi::GstRTSPMediaClass; let f = (*parent_class) .handle_sdp .expect("No `handle_sdp` virtual method implementation in parent class"); gst::result_from_gboolean!( f( self.obj().unsafe_cast_ref::().to_glib_none().0, sdp as *const _ as *mut gst_sdp::ffi::GstSDPMessage ), gst::CAT_RUST, "Parent function `handle_sdp` failed" ) } } } impl RTSPMediaImplExt for T {} unsafe impl IsSubclassable for RTSPMedia { fn class_init(klass: &mut glib::Class) { Self::parent_class_init::(klass); let klass = klass.as_mut(); klass.handle_message = Some(media_handle_message::); klass.prepare = Some(media_prepare::); klass.unprepare = Some(media_unprepare::); klass.suspend = Some(media_suspend::); klass.unsuspend = Some(media_unsuspend::); klass.query_position = Some(media_query_position::); klass.query_stop = Some(media_query_stop::); klass.create_rtpbin = Some(media_create_rtpbin::); klass.setup_rtpbin = Some(media_setup_rtpbin::); klass.setup_sdp = Some(media_setup_sdp::); klass.new_stream = Some(media_new_stream::); klass.removed_stream = Some(media_removed_stream::); klass.prepared = Some(media_prepared::); klass.unprepared = Some(media_unprepared::); klass.target_state = Some(media_target_state::); klass.new_state = Some(media_new_state::); klass.handle_sdp = Some(media_handle_sdp::); } } unsafe extern "C" fn media_handle_message( ptr: *mut ffi::GstRTSPMedia, message: *mut gst::ffi::GstMessage, ) -> glib::ffi::gboolean { let instance = &*(ptr as *mut T::Instance); let imp = instance.imp(); imp.handle_message(gst::MessageRef::from_ptr(message)) .into_glib() } unsafe extern "C" fn media_prepare( ptr: *mut ffi::GstRTSPMedia, thread: *mut ffi::GstRTSPThread, ) -> glib::ffi::gboolean { let instance = &*(ptr as *mut T::Instance); let imp = instance.imp(); match imp.prepare(&from_glib_borrow(thread)) { Ok(()) => glib::ffi::GTRUE, Err(err) => { err.log_with_imp(imp); glib::ffi::GFALSE } } } unsafe extern "C" fn media_unprepare( ptr: *mut ffi::GstRTSPMedia, ) -> glib::ffi::gboolean { let instance = &*(ptr as *mut T::Instance); let imp = instance.imp(); match imp.unprepare() { Ok(()) => glib::ffi::GTRUE, Err(err) => { err.log_with_imp(imp); glib::ffi::GFALSE } } } unsafe extern "C" fn media_suspend( ptr: *mut ffi::GstRTSPMedia, ) -> glib::ffi::gboolean { let instance = &*(ptr as *mut T::Instance); let imp = instance.imp(); match imp.suspend() { Ok(()) => glib::ffi::GTRUE, Err(err) => { err.log_with_imp(imp); glib::ffi::GFALSE } } } unsafe extern "C" fn media_unsuspend( ptr: *mut ffi::GstRTSPMedia, ) -> glib::ffi::gboolean { let instance = &*(ptr as *mut T::Instance); let imp = instance.imp(); match imp.unsuspend() { Ok(()) => glib::ffi::GTRUE, Err(err) => { err.log_with_imp(imp); glib::ffi::GFALSE } } } unsafe extern "C" fn media_query_position( ptr: *mut ffi::GstRTSPMedia, position: *mut i64, ) -> glib::ffi::gboolean { let instance = &*(ptr as *mut T::Instance); let imp = instance.imp(); match imp.query_position() { Some(pos) => { *position = pos.into_glib() as i64; glib::ffi::GTRUE } None => glib::ffi::GFALSE, } } unsafe extern "C" fn media_query_stop( ptr: *mut ffi::GstRTSPMedia, stop: *mut i64, ) -> glib::ffi::gboolean { let instance = &*(ptr as *mut T::Instance); let imp = instance.imp(); match imp.query_stop() { Some(s) => { *stop = s.into_glib() as i64; glib::ffi::GTRUE } None => glib::ffi::GFALSE, } } unsafe extern "C" fn media_create_rtpbin( ptr: *mut ffi::GstRTSPMedia, ) -> *mut gst::ffi::GstElement { let instance = &*(ptr as *mut T::Instance); let imp = instance.imp(); let res: *mut gst::ffi::GstElement = imp.create_rtpbin().into_glib_ptr(); if !res.is_null() { glib::gobject_ffi::g_object_force_floating(res as *mut _); } res } unsafe extern "C" fn media_setup_rtpbin( ptr: *mut ffi::GstRTSPMedia, rtpbin: *mut gst::ffi::GstElement, ) -> glib::ffi::gboolean { let instance = &*(ptr as *mut T::Instance); let imp = instance.imp(); // If the rtpbin was floating before make sure it is not anymore for now so // we don't accidentally take ownership of it somewhere along the line if glib::gobject_ffi::g_object_is_floating(rtpbin as *mut _) != glib::ffi::GFALSE { glib::gobject_ffi::g_object_ref_sink(rtpbin as *mut _); } let res = match imp.setup_rtpbin(&from_glib_borrow(rtpbin)) { Ok(()) => glib::ffi::GTRUE, Err(err) => { err.log_with_imp(imp); glib::ffi::GFALSE } }; // Ensure that the rtpbin is still floating afterwards here glib::gobject_ffi::g_object_force_floating(rtpbin as *mut _); res } unsafe extern "C" fn media_setup_sdp( ptr: *mut ffi::GstRTSPMedia, sdp: *mut gst_sdp::ffi::GstSDPMessage, info: *mut ffi::GstSDPInfo, ) -> glib::ffi::gboolean { let instance = &*(ptr as *mut T::Instance); let imp = instance.imp(); match imp.setup_sdp( &mut *(sdp as *mut gst_sdp::SDPMessageRef), &SDPInfo(ptr::NonNull::new(info).expect("NULL SDPInfo")), ) { Ok(()) => glib::ffi::GTRUE, Err(err) => { err.log_with_imp(imp); glib::ffi::GFALSE } } } unsafe extern "C" fn media_new_stream( ptr: *mut ffi::GstRTSPMedia, stream: *mut ffi::GstRTSPStream, ) { let instance = &*(ptr as *mut T::Instance); let imp = instance.imp(); imp.new_stream(&from_glib_borrow(stream)); } unsafe extern "C" fn media_removed_stream( ptr: *mut ffi::GstRTSPMedia, stream: *mut ffi::GstRTSPStream, ) { let instance = &*(ptr as *mut T::Instance); let imp = instance.imp(); imp.removed_stream(&from_glib_borrow(stream)); } unsafe extern "C" fn media_prepared(ptr: *mut ffi::GstRTSPMedia) { let instance = &*(ptr as *mut T::Instance); let imp = instance.imp(); imp.prepared(); } unsafe extern "C" fn media_unprepared(ptr: *mut ffi::GstRTSPMedia) { let instance = &*(ptr as *mut T::Instance); let imp = instance.imp(); imp.unprepared(); } unsafe extern "C" fn media_target_state( ptr: *mut ffi::GstRTSPMedia, state: gst::ffi::GstState, ) { let instance = &*(ptr as *mut T::Instance); let imp = instance.imp(); imp.target_state(from_glib(state)); } unsafe extern "C" fn media_new_state( ptr: *mut ffi::GstRTSPMedia, state: gst::ffi::GstState, ) { let instance = &*(ptr as *mut T::Instance); let imp = instance.imp(); imp.new_state(from_glib(state)); } unsafe extern "C" fn media_handle_sdp( ptr: *mut ffi::GstRTSPMedia, sdp: *mut gst_sdp::ffi::GstSDPMessage, ) -> glib::ffi::gboolean { let instance = &*(ptr as *mut T::Instance); let imp = instance.imp(); match imp.handle_sdp(&*(sdp as *const gst_sdp::SDPMessageRef)) { Ok(()) => glib::ffi::GTRUE, Err(err) => { err.log_with_imp(imp); glib::ffi::GFALSE } } }