From b49f6320b08411dbdd3d57c4c0e197d25c3ac0e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Sun, 23 Feb 2020 10:00:48 +0200 Subject: [PATCH] rtsp_server: Add subclassing support for MediaFactory and Media --- gstreamer-rtsp-server/Cargo.toml | 2 + gstreamer-rtsp-server/src/lib.rs | 2 + gstreamer-rtsp-server/src/subclass/mod.rs | 16 + .../src/subclass/rtsp_media.rs | 780 ++++++++++++++++++ .../src/subclass/rtsp_media_factory.rs | 338 ++++++++ 5 files changed, 1138 insertions(+) create mode 100644 gstreamer-rtsp-server/src/subclass/mod.rs create mode 100644 gstreamer-rtsp-server/src/subclass/rtsp_media.rs create mode 100644 gstreamer-rtsp-server/src/subclass/rtsp_media_factory.rs diff --git a/gstreamer-rtsp-server/Cargo.toml b/gstreamer-rtsp-server/Cargo.toml index 7ac6d5493..b4ecc39df 100644 --- a/gstreamer-rtsp-server/Cargo.toml +++ b/gstreamer-rtsp-server/Cargo.toml @@ -20,12 +20,14 @@ glib-sys = { git = "https://github.com/gtk-rs/sys" } gio-sys = { git = "https://github.com/gtk-rs/sys" } gobject-sys = { git = "https://github.com/gtk-rs/sys" } gstreamer-sys = { git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs-sys", features = ["v1_8"] } +gstreamer-sdp-sys = { git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs-sys", features = ["v1_8"] } gstreamer-rtsp-sys = { git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs-sys", features = ["v1_8"] } gstreamer-rtsp-server-sys = { git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs-sys", features = ["v1_8"] } gstreamer-net-sys = { git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs-sys", features = ["v1_8"] } glib = { git = "https://github.com/gtk-rs/glib" } gio = { git = "https://github.com/gtk-rs/gio" } gstreamer = { path = "../gstreamer" } +gstreamer-sdp = { path = "../gstreamer-sdp" } gstreamer-rtsp = { path = "../gstreamer-rtsp" } gstreamer-net = { path = "../gstreamer-net" } diff --git a/gstreamer-rtsp-server/src/lib.rs b/gstreamer-rtsp-server/src/lib.rs index aff986db6..598f9fad4 100644 --- a/gstreamer-rtsp-server/src/lib.rs +++ b/gstreamer-rtsp-server/src/lib.rs @@ -25,6 +25,8 @@ extern crate gstreamer_net_sys as gst_net_sys; extern crate gstreamer_rtsp as gst_rtsp; extern crate gstreamer_rtsp_server_sys as gst_rtsp_server_sys; extern crate gstreamer_rtsp_sys as gst_rtsp_sys; +extern crate gstreamer_sdp as gst_sdp; +extern crate gstreamer_sdp_sys as gst_sdp_sys; extern crate gstreamer_sys as gst_sys; macro_rules! assert_initialized_main_thread { diff --git a/gstreamer-rtsp-server/src/subclass/mod.rs b/gstreamer-rtsp-server/src/subclass/mod.rs new file mode 100644 index 000000000..2568a26c7 --- /dev/null +++ b/gstreamer-rtsp-server/src/subclass/mod.rs @@ -0,0 +1,16 @@ +// Copyright (C) 2020 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. +#![allow(clippy::cast_ptr_alignment)] + +pub mod rtsp_media; +pub mod rtsp_media_factory; + +pub mod prelude { + pub use super::rtsp_media::{RTSPMediaImpl, RTSPMediaImplExt}; + pub use super::rtsp_media_factory::{RTSPMediaFactoryImpl, RTSPMediaFactoryImplExt}; +} diff --git a/gstreamer-rtsp-server/src/subclass/rtsp_media.rs b/gstreamer-rtsp-server/src/subclass/rtsp_media.rs new file mode 100644 index 000000000..8ed01479e --- /dev/null +++ b/gstreamer-rtsp-server/src/subclass/rtsp_media.rs @@ -0,0 +1,780 @@ +// Copyright (C) 2020 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 gst_rtsp_server_sys; + +use glib::translate::*; + +use glib::subclass::prelude::*; + +use gst::prelude::*; +use std::ptr; + +use RTSPMedia; +use RTSPMediaClass; +use 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 + 'static { + fn handle_message(&self, media: &RTSPMedia, message: &gst::MessageRef) -> bool { + self.parent_handle_message(media, message) + } + + fn prepare(&self, media: &RTSPMedia, thread: &RTSPThread) -> Result<(), gst::LoggableError> { + self.parent_prepare(media, thread) + } + + fn unprepare(&self, media: &RTSPMedia) -> Result<(), gst::LoggableError> { + self.parent_unprepare(media) + } + + fn suspend(&self, media: &RTSPMedia) -> Result<(), gst::LoggableError> { + self.parent_suspend(media) + } + + fn unsuspend(&self, media: &RTSPMedia) -> Result<(), gst::LoggableError> { + self.parent_unsuspend(media) + } + + // TODO missing: convert_range + + fn query_position(&self, media: &RTSPMedia) -> Option { + self.parent_query_position(media) + } + + fn query_stop(&self, media: &RTSPMedia) -> Option { + self.parent_query_stop(media) + } + + fn create_rtpbin(&self, media: &RTSPMedia) -> Option { + self.parent_create_rtpbin(media) + } + + fn setup_rtpbin( + &self, + media: &RTSPMedia, + rtpbin: &gst::Element, + ) -> Result<(), gst::LoggableError> { + self.parent_setup_rtpbin(media, rtpbin) + } + + fn setup_sdp( + &self, + media: &RTSPMedia, + sdp: &mut gst_sdp::SDPMessageRef, + info: &SDPInfo, + ) -> Result<(), gst::LoggableError> { + self.parent_setup_sdp(media, sdp, info) + } + + fn new_stream(&self, media: &RTSPMedia, stream: &::RTSPStream) { + self.parent_new_stream(media, stream); + } + + fn removed_stream(&self, media: &RTSPMedia, stream: &::RTSPStream) { + self.parent_removed_stream(media, stream); + } + + fn prepared(&self, media: &RTSPMedia) { + self.parent_prepared(media); + } + + fn unprepared(&self, media: &RTSPMedia) { + self.parent_unprepared(media); + } + + fn target_state(&self, media: &RTSPMedia, state: gst::State) { + self.parent_target_state(media, state); + } + + fn new_state(&self, media: &RTSPMedia, state: gst::State) { + self.parent_new_state(media, state); + } + + fn handle_sdp( + &self, + media: &RTSPMedia, + sdp: &gst_sdp::SDPMessageRef, + ) -> Result<(), gst::LoggableError> { + self.parent_handle_sdp(media, sdp) + } +} + +pub trait RTSPMediaImplExt { + fn parent_handle_message(&self, media: &RTSPMedia, message: &gst::MessageRef) -> bool; + fn parent_prepare( + &self, + media: &RTSPMedia, + thread: &RTSPThread, + ) -> Result<(), gst::LoggableError>; + fn parent_unprepare(&self, media: &RTSPMedia) -> Result<(), gst::LoggableError>; + fn parent_suspend(&self, media: &RTSPMedia) -> Result<(), gst::LoggableError>; + fn parent_unsuspend(&self, media: &RTSPMedia) -> Result<(), gst::LoggableError>; + // TODO missing: convert_range + + fn parent_query_position(&self, media: &RTSPMedia) -> Option; + fn parent_query_stop(&self, media: &RTSPMedia) -> Option; + fn parent_create_rtpbin(&self, media: &RTSPMedia) -> Option; + fn parent_setup_rtpbin( + &self, + media: &RTSPMedia, + rtpbin: &gst::Element, + ) -> Result<(), gst::LoggableError>; + fn parent_setup_sdp( + &self, + media: &RTSPMedia, + sdp: &mut gst_sdp::SDPMessageRef, + info: &SDPInfo, + ) -> Result<(), gst::LoggableError>; + fn parent_new_stream(&self, media: &RTSPMedia, stream: &::RTSPStream); + fn parent_removed_stream(&self, media: &RTSPMedia, stream: &::RTSPStream); + fn parent_prepared(&self, media: &RTSPMedia); + fn parent_unprepared(&self, media: &RTSPMedia); + fn parent_target_state(&self, media: &RTSPMedia, state: gst::State); + fn parent_new_state(&self, media: &RTSPMedia, state: gst::State); + fn parent_handle_sdp( + &self, + media: &RTSPMedia, + sdp: &gst_sdp::SDPMessageRef, + ) -> Result<(), gst::LoggableError>; +} + +impl RTSPMediaImplExt for T { + fn parent_handle_message(&self, media: &RTSPMedia, message: &gst::MessageRef) -> bool { + unsafe { + let data = self.get_type_data(); + let parent_class = + data.as_ref().get_parent_class() as *mut gst_rtsp_server_sys::GstRTSPMediaClass; + if let Some(f) = (*parent_class).handle_message { + from_glib(f(media.to_glib_none().0, message.as_ptr() as *mut _)) + } else { + false + } + } + } + + fn parent_prepare( + &self, + media: &RTSPMedia, + thread: &RTSPThread, + ) -> Result<(), gst::LoggableError> { + unsafe { + let data = self.get_type_data(); + let parent_class = + data.as_ref().get_parent_class() as *mut gst_rtsp_server_sys::GstRTSPMediaClass; + if let Some(f) = (*parent_class).prepare { + gst_result_from_gboolean!( + f(media.to_glib_none().0, thread.to_glib_none().0), + gst::CAT_RUST, + "Parent function `prepare` failed" + ) + } else { + Ok(()) + } + } + } + + fn parent_unprepare(&self, media: &RTSPMedia) -> Result<(), gst::LoggableError> { + unsafe { + let data = self.get_type_data(); + let parent_class = + data.as_ref().get_parent_class() as *mut gst_rtsp_server_sys::GstRTSPMediaClass; + if let Some(f) = (*parent_class).unprepare { + gst_result_from_gboolean!( + f(media.to_glib_none().0), + gst::CAT_RUST, + "Parent function `unprepare` failed" + ) + } else { + Ok(()) + } + } + } + + fn parent_suspend(&self, media: &RTSPMedia) -> Result<(), gst::LoggableError> { + unsafe { + let data = self.get_type_data(); + let parent_class = + data.as_ref().get_parent_class() as *mut gst_rtsp_server_sys::GstRTSPMediaClass; + if let Some(f) = (*parent_class).suspend { + gst_result_from_gboolean!( + f(media.to_glib_none().0), + gst::CAT_RUST, + "Parent function `suspend` failed" + ) + } else { + Ok(()) + } + } + } + + fn parent_unsuspend(&self, media: &RTSPMedia) -> Result<(), gst::LoggableError> { + unsafe { + let data = self.get_type_data(); + let parent_class = + data.as_ref().get_parent_class() as *mut gst_rtsp_server_sys::GstRTSPMediaClass; + if let Some(f) = (*parent_class).unsuspend { + gst_result_from_gboolean!( + f(media.to_glib_none().0), + gst::CAT_RUST, + "Parent function `unsuspend` failed" + ) + } else { + Ok(()) + } + } + } + + // TODO missing: convert_range + + fn parent_query_position(&self, media: &RTSPMedia) -> Option { + unsafe { + use std::mem; + + let data = self.get_type_data(); + let parent_class = + data.as_ref().get_parent_class() as *mut gst_rtsp_server_sys::GstRTSPMediaClass; + if let Some(f) = (*parent_class).query_position { + let mut position = mem::MaybeUninit::uninit(); + if f(media.to_glib_none().0, position.as_mut_ptr()) == glib_sys::GFALSE { + None + } else { + Some(from_glib(position.assume_init() as u64)) + } + } else { + None + } + } + } + + fn parent_query_stop(&self, media: &RTSPMedia) -> Option { + unsafe { + use std::mem; + + let data = self.get_type_data(); + let parent_class = + data.as_ref().get_parent_class() as *mut gst_rtsp_server_sys::GstRTSPMediaClass; + if let Some(f) = (*parent_class).query_stop { + let mut stop = mem::MaybeUninit::uninit(); + if f(media.to_glib_none().0, stop.as_mut_ptr()) == glib_sys::GFALSE { + None + } else { + Some(from_glib(stop.assume_init() as u64)) + } + } else { + None + } + } + } + + fn parent_create_rtpbin(&self, media: &RTSPMedia) -> Option { + unsafe { + let data = self.get_type_data(); + let parent_class = + data.as_ref().get_parent_class() as *mut gst_rtsp_server_sys::GstRTSPMediaClass; + let f = (*parent_class) + .create_rtpbin + .expect("No `create_rtpbin` virtual method implementation in parent class"); + + from_glib_none(f(media.to_glib_none().0)) + } + } + + fn parent_setup_rtpbin( + &self, + media: &RTSPMedia, + rtpbin: &gst::Element, + ) -> Result<(), gst::LoggableError> { + unsafe { + let data = self.get_type_data(); + let parent_class = + data.as_ref().get_parent_class() as *mut gst_rtsp_server_sys::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 + gobject_sys::g_object_force_floating(ptr as *mut _); + + let res = gst_result_from_gboolean!( + f(media.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 gobject_sys::g_object_is_floating(ptr as *mut _) != glib_sys::GFALSE { + gobject_sys::g_object_ref_sink(ptr as *mut _); + } + + res + } else { + Ok(()) + } + } + } + + fn parent_setup_sdp( + &self, + media: &RTSPMedia, + sdp: &mut gst_sdp::SDPMessageRef, + info: &SDPInfo, + ) -> Result<(), gst::LoggableError> { + unsafe { + let data = self.get_type_data(); + let parent_class = + data.as_ref().get_parent_class() as *mut gst_rtsp_server_sys::GstRTSPMediaClass; + let f = (*parent_class) + .setup_sdp + .expect("No `setup_sdp` virtual method implementation in parent class"); + + gst_result_from_gboolean!( + f( + media.to_glib_none().0, + sdp as *mut _ as *mut gst_sdp_sys::GstSDPMessage, + info.0.as_ptr() + ), + gst::CAT_RUST, + "Parent function `setup_sdp` failed" + ) + } + } + + fn parent_new_stream(&self, media: &RTSPMedia, stream: &::RTSPStream) { + unsafe { + let data = self.get_type_data(); + let parent_class = + data.as_ref().get_parent_class() as *mut gst_rtsp_server_sys::GstRTSPMediaClass; + if let Some(f) = (*parent_class).new_stream { + f(media.to_glib_none().0, stream.to_glib_none().0); + } + } + } + + fn parent_removed_stream(&self, media: &RTSPMedia, stream: &::RTSPStream) { + unsafe { + let data = self.get_type_data(); + let parent_class = + data.as_ref().get_parent_class() as *mut gst_rtsp_server_sys::GstRTSPMediaClass; + if let Some(f) = (*parent_class).removed_stream { + f(media.to_glib_none().0, stream.to_glib_none().0); + } + } + } + + fn parent_prepared(&self, media: &RTSPMedia) { + unsafe { + let data = self.get_type_data(); + let parent_class = + data.as_ref().get_parent_class() as *mut gst_rtsp_server_sys::GstRTSPMediaClass; + if let Some(f) = (*parent_class).prepared { + f(media.to_glib_none().0); + } + } + } + + fn parent_unprepared(&self, media: &RTSPMedia) { + unsafe { + let data = self.get_type_data(); + let parent_class = + data.as_ref().get_parent_class() as *mut gst_rtsp_server_sys::GstRTSPMediaClass; + if let Some(f) = (*parent_class).unprepared { + f(media.to_glib_none().0); + } + } + } + + fn parent_target_state(&self, media: &RTSPMedia, state: gst::State) { + unsafe { + let data = self.get_type_data(); + let parent_class = + data.as_ref().get_parent_class() as *mut gst_rtsp_server_sys::GstRTSPMediaClass; + if let Some(f) = (*parent_class).target_state { + f(media.to_glib_none().0, state.to_glib()); + } + } + } + + fn parent_new_state(&self, media: &RTSPMedia, state: gst::State) { + unsafe { + let data = self.get_type_data(); + let parent_class = + data.as_ref().get_parent_class() as *mut gst_rtsp_server_sys::GstRTSPMediaClass; + if let Some(f) = (*parent_class).new_state { + f(media.to_glib_none().0, state.to_glib()); + } + } + } + + fn parent_handle_sdp( + &self, + media: &RTSPMedia, + sdp: &gst_sdp::SDPMessageRef, + ) -> Result<(), gst::LoggableError> { + unsafe { + let data = self.get_type_data(); + let parent_class = + data.as_ref().get_parent_class() as *mut gst_rtsp_server_sys::GstRTSPMediaClass; + let f = (*parent_class) + .handle_sdp + .expect("No `handle_sdp` virtual method implementation in parent class"); + + gst_result_from_gboolean!( + f( + media.to_glib_none().0, + sdp as *const _ as *mut gst_sdp_sys::GstSDPMessage + ), + gst::CAT_RUST, + "Parent function `handle_sdp` failed" + ) + } + } +} +unsafe impl IsSubclassable for RTSPMediaClass { + fn override_vfuncs(&mut self) { + >::override_vfuncs(self); + unsafe { + let klass = &mut *(self as *mut Self as *mut gst_rtsp_server_sys::GstRTSPMediaClass); + 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 gst_rtsp_server_sys::GstRTSPMedia, + message: *mut gst_sys::GstMessage, +) -> glib_sys::gboolean +where + T: RTSPMediaImpl, +{ + let instance = &*(ptr as *mut T::Instance); + let imp = instance.get_impl(); + let wrap: RTSPMedia = from_glib_borrow(ptr); + + imp.handle_message(&wrap, gst::MessageRef::from_ptr(message)) + .to_glib() +} + +unsafe extern "C" fn media_prepare( + ptr: *mut gst_rtsp_server_sys::GstRTSPMedia, + thread: *mut gst_rtsp_server_sys::GstRTSPThread, +) -> glib_sys::gboolean +where + T: RTSPMediaImpl, +{ + let instance = &*(ptr as *mut T::Instance); + let imp = instance.get_impl(); + let wrap: RTSPMedia = from_glib_borrow(ptr); + + match imp.prepare(&wrap, &from_glib_borrow(thread)) { + Ok(()) => glib_sys::GTRUE, + Err(err) => { + err.log_with_object(&wrap); + glib_sys::GFALSE + } + } +} + +unsafe extern "C" fn media_unprepare( + ptr: *mut gst_rtsp_server_sys::GstRTSPMedia, +) -> glib_sys::gboolean +where + T: RTSPMediaImpl, +{ + let instance = &*(ptr as *mut T::Instance); + let imp = instance.get_impl(); + let wrap: RTSPMedia = from_glib_borrow(ptr); + + match imp.unprepare(&wrap) { + Ok(()) => glib_sys::GTRUE, + Err(err) => { + err.log_with_object(&wrap); + glib_sys::GFALSE + } + } +} + +unsafe extern "C" fn media_suspend( + ptr: *mut gst_rtsp_server_sys::GstRTSPMedia, +) -> glib_sys::gboolean +where + T: RTSPMediaImpl, +{ + let instance = &*(ptr as *mut T::Instance); + let imp = instance.get_impl(); + let wrap: RTSPMedia = from_glib_borrow(ptr); + + match imp.suspend(&wrap) { + Ok(()) => glib_sys::GTRUE, + Err(err) => { + err.log_with_object(&wrap); + glib_sys::GFALSE + } + } +} + +unsafe extern "C" fn media_unsuspend( + ptr: *mut gst_rtsp_server_sys::GstRTSPMedia, +) -> glib_sys::gboolean +where + T: RTSPMediaImpl, +{ + let instance = &*(ptr as *mut T::Instance); + let imp = instance.get_impl(); + let wrap: RTSPMedia = from_glib_borrow(ptr); + + match imp.unsuspend(&wrap) { + Ok(()) => glib_sys::GTRUE, + Err(err) => { + err.log_with_object(&wrap); + glib_sys::GFALSE + } + } +} + +unsafe extern "C" fn media_query_position( + ptr: *mut gst_rtsp_server_sys::GstRTSPMedia, + position: *mut i64, +) -> glib_sys::gboolean +where + T: RTSPMediaImpl, +{ + let instance = &*(ptr as *mut T::Instance); + let imp = instance.get_impl(); + let wrap: RTSPMedia = from_glib_borrow(ptr); + + match imp.query_position(&wrap) { + Some(pos) => { + *position = pos.to_glib() as i64; + glib_sys::GTRUE + } + None => glib_sys::GFALSE, + } +} + +unsafe extern "C" fn media_query_stop( + ptr: *mut gst_rtsp_server_sys::GstRTSPMedia, + stop: *mut i64, +) -> glib_sys::gboolean +where + T: RTSPMediaImpl, +{ + let instance = &*(ptr as *mut T::Instance); + let imp = instance.get_impl(); + let wrap: RTSPMedia = from_glib_borrow(ptr); + + match imp.query_stop(&wrap) { + Some(s) => { + *stop = s.to_glib() as i64; + glib_sys::GTRUE + } + None => glib_sys::GFALSE, + } +} + +unsafe extern "C" fn media_create_rtpbin( + ptr: *mut gst_rtsp_server_sys::GstRTSPMedia, +) -> *mut gst_sys::GstElement +where + T: RTSPMediaImpl, +{ + let instance = &*(ptr as *mut T::Instance); + let imp = instance.get_impl(); + let wrap: RTSPMedia = from_glib_borrow(ptr); + + let res: *mut gst_sys::GstElement = imp.create_rtpbin(&wrap).to_glib_full(); + + if !res.is_null() { + gobject_sys::g_object_force_floating(res as *mut _); + } + + res +} + +unsafe extern "C" fn media_setup_rtpbin( + ptr: *mut gst_rtsp_server_sys::GstRTSPMedia, + rtpbin: *mut gst_sys::GstElement, +) -> glib_sys::gboolean +where + T: RTSPMediaImpl, +{ + let instance = &*(ptr as *mut T::Instance); + let imp = instance.get_impl(); + let wrap: RTSPMedia = from_glib_borrow(ptr); + + // 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 gobject_sys::g_object_is_floating(rtpbin as *mut _) != glib_sys::GFALSE { + gobject_sys::g_object_ref_sink(rtpbin as *mut _); + } + + let res = match imp.setup_rtpbin(&wrap, &from_glib_borrow(rtpbin)) { + Ok(()) => glib_sys::GTRUE, + Err(err) => { + err.log_with_object(&wrap); + glib_sys::GFALSE + } + }; + + // Ensure that the rtpbin is still floating afterwards here + gobject_sys::g_object_force_floating(rtpbin as *mut _); + + res +} + +unsafe extern "C" fn media_setup_sdp( + ptr: *mut gst_rtsp_server_sys::GstRTSPMedia, + sdp: *mut gst_sdp_sys::GstSDPMessage, + info: *mut gst_rtsp_server_sys::GstSDPInfo, +) -> glib_sys::gboolean +where + T: RTSPMediaImpl, +{ + let instance = &*(ptr as *mut T::Instance); + let imp = instance.get_impl(); + let wrap: RTSPMedia = from_glib_borrow(ptr); + + match imp.setup_sdp( + &wrap, + &mut *(sdp as *mut gst_sdp::SDPMessageRef), + &SDPInfo(ptr::NonNull::new(info).expect("NULL SDPInfo")), + ) { + Ok(()) => glib_sys::GTRUE, + Err(err) => { + err.log_with_object(&wrap); + glib_sys::GFALSE + } + } +} + +unsafe extern "C" fn media_new_stream( + ptr: *mut gst_rtsp_server_sys::GstRTSPMedia, + stream: *mut gst_rtsp_server_sys::GstRTSPStream, +) where + T: RTSPMediaImpl, +{ + let instance = &*(ptr as *mut T::Instance); + let imp = instance.get_impl(); + let wrap: RTSPMedia = from_glib_borrow(ptr); + + imp.new_stream(&wrap, &from_glib_borrow(stream)); +} + +unsafe extern "C" fn media_removed_stream( + ptr: *mut gst_rtsp_server_sys::GstRTSPMedia, + stream: *mut gst_rtsp_server_sys::GstRTSPStream, +) where + T: RTSPMediaImpl, +{ + let instance = &*(ptr as *mut T::Instance); + let imp = instance.get_impl(); + let wrap: RTSPMedia = from_glib_borrow(ptr); + + imp.removed_stream(&wrap, &from_glib_borrow(stream)); +} + +unsafe extern "C" fn media_prepared(ptr: *mut gst_rtsp_server_sys::GstRTSPMedia) +where + T: RTSPMediaImpl, +{ + let instance = &*(ptr as *mut T::Instance); + let imp = instance.get_impl(); + let wrap: RTSPMedia = from_glib_borrow(ptr); + + imp.prepared(&wrap); +} + +unsafe extern "C" fn media_unprepared( + ptr: *mut gst_rtsp_server_sys::GstRTSPMedia, +) where + T: RTSPMediaImpl, +{ + let instance = &*(ptr as *mut T::Instance); + let imp = instance.get_impl(); + let wrap: RTSPMedia = from_glib_borrow(ptr); + + imp.unprepared(&wrap); +} + +unsafe extern "C" fn media_target_state( + ptr: *mut gst_rtsp_server_sys::GstRTSPMedia, + state: gst_sys::GstState, +) where + T: RTSPMediaImpl, +{ + let instance = &*(ptr as *mut T::Instance); + let imp = instance.get_impl(); + let wrap: RTSPMedia = from_glib_borrow(ptr); + + imp.target_state(&wrap, from_glib(state)); +} + +unsafe extern "C" fn media_new_state( + ptr: *mut gst_rtsp_server_sys::GstRTSPMedia, + state: gst_sys::GstState, +) where + T: RTSPMediaImpl, +{ + let instance = &*(ptr as *mut T::Instance); + let imp = instance.get_impl(); + let wrap: RTSPMedia = from_glib_borrow(ptr); + + imp.new_state(&wrap, from_glib(state)); +} + +unsafe extern "C" fn media_handle_sdp( + ptr: *mut gst_rtsp_server_sys::GstRTSPMedia, + sdp: *mut gst_sdp_sys::GstSDPMessage, +) -> glib_sys::gboolean +where + T: RTSPMediaImpl, +{ + let instance = &*(ptr as *mut T::Instance); + let imp = instance.get_impl(); + let wrap: RTSPMedia = from_glib_borrow(ptr); + + match imp.handle_sdp(&wrap, &*(sdp as *const gst_sdp::SDPMessageRef)) { + Ok(()) => glib_sys::GTRUE, + Err(err) => { + err.log_with_object(&wrap); + glib_sys::GFALSE + } + } +} diff --git a/gstreamer-rtsp-server/src/subclass/rtsp_media_factory.rs b/gstreamer-rtsp-server/src/subclass/rtsp_media_factory.rs new file mode 100644 index 000000000..13bd52050 --- /dev/null +++ b/gstreamer-rtsp-server/src/subclass/rtsp_media_factory.rs @@ -0,0 +1,338 @@ +// Copyright (C) 2020 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 gst_rtsp_server_sys; + +use glib::translate::*; +use gst_rtsp; + +use glib::subclass::prelude::*; + +use RTSPMediaFactory; +use RTSPMediaFactoryClass; + +pub trait RTSPMediaFactoryImpl: + RTSPMediaFactoryImplExt + ObjectImpl + Send + Sync + 'static +{ + fn gen_key( + &self, + factory: &RTSPMediaFactory, + url: &gst_rtsp::RTSPUrl, + ) -> Option { + self.parent_gen_key(factory, url) + } + + fn create_element( + &self, + factory: &RTSPMediaFactory, + url: &gst_rtsp::RTSPUrl, + ) -> Option { + self.parent_create_element(factory, url) + } + + fn construct( + &self, + factory: &RTSPMediaFactory, + url: &gst_rtsp::RTSPUrl, + ) -> Option<::RTSPMedia> { + self.parent_construct(factory, url) + } + + fn create_pipeline( + &self, + factory: &RTSPMediaFactory, + media: &::RTSPMedia, + ) -> Option { + self.parent_create_pipeline(factory, media) + } + + fn configure(&self, factory: &RTSPMediaFactory, media: &::RTSPMedia) { + self.parent_configure(factory, media) + } + + fn media_constructed(&self, factory: &RTSPMediaFactory, media: &::RTSPMedia) { + self.parent_media_constructed(factory, media) + } + + fn media_configure(&self, factory: &RTSPMediaFactory, media: &::RTSPMedia) { + self.parent_media_configure(factory, media) + } +} + +pub trait RTSPMediaFactoryImplExt { + fn parent_gen_key( + &self, + factory: &RTSPMediaFactory, + url: &gst_rtsp::RTSPUrl, + ) -> Option; + + fn parent_create_element( + &self, + factory: &RTSPMediaFactory, + url: &gst_rtsp::RTSPUrl, + ) -> Option; + + fn parent_construct( + &self, + factory: &RTSPMediaFactory, + url: &gst_rtsp::RTSPUrl, + ) -> Option<::RTSPMedia>; + + fn parent_create_pipeline( + &self, + factory: &RTSPMediaFactory, + media: &::RTSPMedia, + ) -> Option; + + fn parent_configure(&self, factory: &RTSPMediaFactory, media: &::RTSPMedia); + + fn parent_media_constructed(&self, factory: &RTSPMediaFactory, media: &::RTSPMedia); + fn parent_media_configure(&self, factory: &RTSPMediaFactory, media: &::RTSPMedia); +} + +impl RTSPMediaFactoryImplExt for T { + fn parent_gen_key( + &self, + factory: &RTSPMediaFactory, + url: &gst_rtsp::RTSPUrl, + ) -> Option { + unsafe { + let data = self.get_type_data(); + let parent_class = data.as_ref().get_parent_class() + as *mut gst_rtsp_server_sys::GstRTSPMediaFactoryClass; + (*parent_class) + .gen_key + .map(|f| from_glib_full(f(factory.to_glib_none().0, url.to_glib_none().0))) + .unwrap_or(None) + } + } + + fn parent_create_element( + &self, + factory: &RTSPMediaFactory, + url: &gst_rtsp::RTSPUrl, + ) -> Option { + unsafe { + let data = self.get_type_data(); + let parent_class = data.as_ref().get_parent_class() + as *mut gst_rtsp_server_sys::GstRTSPMediaFactoryClass; + (*parent_class) + .create_element + .map(|f| from_glib_none(f(factory.to_glib_none().0, url.to_glib_none().0))) + .unwrap_or(None) + } + } + + fn parent_construct( + &self, + factory: &RTSPMediaFactory, + url: &gst_rtsp::RTSPUrl, + ) -> Option<::RTSPMedia> { + unsafe { + let data = self.get_type_data(); + let parent_class = data.as_ref().get_parent_class() + as *mut gst_rtsp_server_sys::GstRTSPMediaFactoryClass; + (*parent_class) + .construct + .map(|f| from_glib_full(f(factory.to_glib_none().0, url.to_glib_none().0))) + .unwrap_or(None) + } + } + + fn parent_create_pipeline( + &self, + factory: &RTSPMediaFactory, + media: &::RTSPMedia, + ) -> Option { + unsafe { + let data = self.get_type_data(); + let parent_class = data.as_ref().get_parent_class() + as *mut gst_rtsp_server_sys::GstRTSPMediaFactoryClass; + (*parent_class) + .create_pipeline + .map(|f| { + let ptr = f(factory.to_glib_none().0, media.to_glib_none().0) + as *mut gst_sys::GstPipeline; + + // See https://gitlab.freedesktop.org/gstreamer/gst-rtsp-server/merge_requests/109 + if gobject_sys::g_object_is_floating(ptr as *mut _) != glib_sys::GFALSE { + gobject_sys::g_object_ref_sink(ptr as *mut _); + } + from_glib_none(ptr) + }) + .unwrap_or(None) + } + } + + fn parent_configure(&self, factory: &RTSPMediaFactory, media: &::RTSPMedia) { + unsafe { + let data = self.get_type_data(); + let parent_class = data.as_ref().get_parent_class() + as *mut gst_rtsp_server_sys::GstRTSPMediaFactoryClass; + if let Some(f) = (*parent_class).configure { + f(factory.to_glib_none().0, media.to_glib_none().0); + } + } + } + + fn parent_media_constructed(&self, factory: &RTSPMediaFactory, media: &::RTSPMedia) { + unsafe { + let data = self.get_type_data(); + let parent_class = data.as_ref().get_parent_class() + as *mut gst_rtsp_server_sys::GstRTSPMediaFactoryClass; + if let Some(f) = (*parent_class).media_constructed { + f(factory.to_glib_none().0, media.to_glib_none().0); + } + } + } + + fn parent_media_configure(&self, factory: &RTSPMediaFactory, media: &::RTSPMedia) { + unsafe { + let data = self.get_type_data(); + let parent_class = data.as_ref().get_parent_class() + as *mut gst_rtsp_server_sys::GstRTSPMediaFactoryClass; + if let Some(f) = (*parent_class).media_configure { + f(factory.to_glib_none().0, media.to_glib_none().0); + } + } + } +} +unsafe impl IsSubclassable for RTSPMediaFactoryClass { + fn override_vfuncs(&mut self) { + >::override_vfuncs(self); + unsafe { + let klass = + &mut *(self as *mut Self as *mut gst_rtsp_server_sys::GstRTSPMediaFactoryClass); + klass.gen_key = Some(factory_gen_key::); + klass.create_element = Some(factory_create_element::); + klass.construct = Some(factory_construct::); + klass.create_pipeline = Some(factory_create_pipeline::); + klass.configure = Some(factory_configure::); + klass.media_constructed = Some(factory_media_constructed::); + klass.media_configure = Some(factory_media_configure::); + } + } +} + +unsafe extern "C" fn factory_gen_key( + ptr: *mut gst_rtsp_server_sys::GstRTSPMediaFactory, + url: *const gst_rtsp_sys::GstRTSPUrl, +) -> *mut std::os::raw::c_char +where + T: RTSPMediaFactoryImpl, +{ + let instance = &*(ptr as *mut T::Instance); + let imp = instance.get_impl(); + let wrap: RTSPMediaFactory = from_glib_borrow(ptr); + + imp.gen_key(&wrap, &from_glib_borrow(url)).to_glib_full() +} + +unsafe extern "C" fn factory_create_element( + ptr: *mut gst_rtsp_server_sys::GstRTSPMediaFactory, + url: *const gst_rtsp_sys::GstRTSPUrl, +) -> *mut gst_sys::GstElement +where + T: RTSPMediaFactoryImpl, +{ + let instance = &*(ptr as *mut T::Instance); + let imp = instance.get_impl(); + let wrap: RTSPMediaFactory = from_glib_borrow(ptr); + + let element = imp + .create_element(&wrap, &from_glib_borrow(url)) + .to_glib_full(); + gobject_sys::g_object_force_floating(element as *mut _); + element +} + +unsafe extern "C" fn factory_construct( + ptr: *mut gst_rtsp_server_sys::GstRTSPMediaFactory, + url: *const gst_rtsp_sys::GstRTSPUrl, +) -> *mut gst_rtsp_server_sys::GstRTSPMedia +where + T: RTSPMediaFactoryImpl, +{ + let instance = &*(ptr as *mut T::Instance); + let imp = instance.get_impl(); + let wrap: RTSPMediaFactory = from_glib_borrow(ptr); + + imp.construct(&wrap, &from_glib_borrow(url)).to_glib_full() +} + +unsafe extern "C" fn factory_create_pipeline( + ptr: *mut gst_rtsp_server_sys::GstRTSPMediaFactory, + media: *mut gst_rtsp_server_sys::GstRTSPMedia, +) -> *mut gst_sys::GstElement +where + T: RTSPMediaFactoryImpl, +{ + use once_cell::sync::Lazy; + use std::mem; + + static PIPELINE_QUARK: Lazy = + Lazy::new(|| glib::Quark::from_string("gstreamer-rs-rtsp-media-pipeline")); + + let instance = &*(ptr as *mut T::Instance); + let imp = instance.get_impl(); + let wrap: RTSPMediaFactory = from_glib_borrow(ptr); + + let pipeline: *mut gst_sys::GstPipeline = imp + .create_pipeline(&wrap, &from_glib_borrow(media)) + .to_glib_none() + .0; + + // FIXME We somehow need to ensure the pipeline actually stays alive... + gobject_sys::g_object_set_qdata_full( + media as *mut _, + PIPELINE_QUARK.to_glib(), + pipeline as *mut _, + Some(mem::transmute(gobject_sys::g_object_unref as usize)), + ); + + pipeline as *mut _ +} + +unsafe extern "C" fn factory_configure( + ptr: *mut gst_rtsp_server_sys::GstRTSPMediaFactory, + media: *mut gst_rtsp_server_sys::GstRTSPMedia, +) where + T: RTSPMediaFactoryImpl, +{ + let instance = &*(ptr as *mut T::Instance); + let imp = instance.get_impl(); + let wrap: RTSPMediaFactory = from_glib_borrow(ptr); + + imp.configure(&wrap, &from_glib_borrow(media)); +} + +unsafe extern "C" fn factory_media_constructed( + ptr: *mut gst_rtsp_server_sys::GstRTSPMediaFactory, + media: *mut gst_rtsp_server_sys::GstRTSPMedia, +) where + T: RTSPMediaFactoryImpl, +{ + let instance = &*(ptr as *mut T::Instance); + let imp = instance.get_impl(); + let wrap: RTSPMediaFactory = from_glib_borrow(ptr); + + imp.media_constructed(&wrap, &from_glib_borrow(media)); +} + +unsafe extern "C" fn factory_media_configure( + ptr: *mut gst_rtsp_server_sys::GstRTSPMediaFactory, + media: *mut gst_rtsp_server_sys::GstRTSPMedia, +) where + T: RTSPMediaFactoryImpl, +{ + let instance = &*(ptr as *mut T::Instance); + let imp = instance.get_impl(); + let wrap: RTSPMediaFactory = from_glib_borrow(ptr); + + imp.media_configure(&wrap, &from_glib_borrow(media)); +}