diff --git a/docs/plugins/gst_plugins_cache.json b/docs/plugins/gst_plugins_cache.json index 6cdfe0b2..bec11184 100644 --- a/docs/plugins/gst_plugins_cache.json +++ b/docs/plugins/gst_plugins_cache.json @@ -6440,6 +6440,7 @@ "description": "WebRTC src", "hierarchy": [ "GstWebRTCSrc", + "GstBaseWebRTCSrc", "GstBin", "GstElement", "GstObject", @@ -6466,108 +6467,7 @@ "type": "GstWebRTCSrcPad" } }, - "properties": { - "audio-codecs": { - "blurb": "Names of audio codecs to be be used during the SDP negotiation. Valid values: [OPUS]", - "conditionally-available": false, - "construct": false, - "construct-only": false, - "controllable": false, - "mutable": "ready", - "readable": true, - "type": "GstValueArray", - "writable": true - }, - "enable-data-channel-navigation": { - "blurb": "Enable navigation events through a dedicated WebRTCDataChannel", - "conditionally-available": false, - "construct": false, - "construct-only": false, - "controllable": false, - "default": "false", - "mutable": "ready", - "readable": true, - "type": "gboolean", - "writable": true - }, - "meta": { - "blurb": "Free form metadata about the consumer", - "conditionally-available": false, - "construct": false, - "construct-only": false, - "controllable": false, - "mutable": "ready", - "readable": true, - "type": "GstStructure", - "writable": true - }, - "signaller": { - "blurb": "The Signallable object to use to handle WebRTC Signalling", - "conditionally-available": false, - "construct": false, - "construct-only": true, - "controllable": false, - "mutable": "null", - "readable": true, - "type": "GstRSWebRTCSignallableIface", - "writable": true - }, - "stun-server": { - "blurb": "The STUN server of the form stun://host:port", - "conditionally-available": false, - "construct": false, - "construct-only": false, - "controllable": false, - "default": "stun://stun.l.google.com:19302", - "mutable": "ready", - "readable": true, - "type": "gchararray", - "writable": true - }, - "turn-servers": { - "blurb": "The TURN servers of the form <\"turn(s)://username:password@host:port\", \"turn(s)://username1:password1@host1:port1\">", - "conditionally-available": false, - "construct": false, - "construct-only": false, - "controllable": false, - "mutable": "ready", - "readable": true, - "type": "GstValueArray", - "writable": true - }, - "video-codecs": { - "blurb": "Names of video codecs to be be used during the SDP negotiation. Valid values: [VP8, H264, VP9, H265]", - "conditionally-available": false, - "construct": false, - "construct-only": false, - "controllable": false, - "mutable": "ready", - "readable": true, - "type": "GstValueArray", - "writable": true - } - }, - "rank": "primary", - "signals": { - "request-encoded-filter": { - "args": [ - { - "name": "arg0", - "type": "gchararray" - }, - { - "name": "arg1", - "type": "gchararray" - }, - { - "name": "arg2", - "type": "GstCaps" - } - ], - "return-type": "GstElement", - "when": "last" - } - } + "rank": "primary" }, "whipclientsink": { "author": "Taruntej Kanakamalla ", @@ -6890,6 +6790,121 @@ } } }, + "GstBaseWebRTCSrc": { + "hierarchy": [ + "GstBaseWebRTCSrc", + "GstBin", + "GstElement", + "GstObject", + "GInitiallyUnowned", + "GObject" + ], + "interfaces": [ + "GstChildProxy" + ], + "kind": "object", + "properties": { + "audio-codecs": { + "blurb": "Names of audio codecs to be be used during the SDP negotiation. Valid values: [OPUS]", + "conditionally-available": false, + "construct": false, + "construct-only": false, + "controllable": false, + "mutable": "ready", + "readable": true, + "type": "GstValueArray", + "writable": true + }, + "enable-data-channel-navigation": { + "blurb": "Enable navigation events through a dedicated WebRTCDataChannel", + "conditionally-available": false, + "construct": false, + "construct-only": false, + "controllable": false, + "default": "false", + "mutable": "ready", + "readable": true, + "type": "gboolean", + "writable": true + }, + "meta": { + "blurb": "Free form metadata about the consumer", + "conditionally-available": false, + "construct": false, + "construct-only": false, + "controllable": false, + "mutable": "ready", + "readable": true, + "type": "GstStructure", + "writable": true + }, + "signaller": { + "blurb": "The Signallable object to use to handle WebRTC Signalling", + "conditionally-available": false, + "construct": false, + "construct-only": true, + "controllable": false, + "mutable": "null", + "readable": true, + "type": "GstRSWebRTCSignallableIface", + "writable": true + }, + "stun-server": { + "blurb": "The STUN server of the form stun://host:port", + "conditionally-available": false, + "construct": false, + "construct-only": false, + "controllable": false, + "default": "stun://stun.l.google.com:19302", + "mutable": "ready", + "readable": true, + "type": "gchararray", + "writable": true + }, + "turn-servers": { + "blurb": "The TURN servers of the form <\"turn(s)://username:password@host:port\", \"turn(s)://username1:password1@host1:port1\">", + "conditionally-available": false, + "construct": false, + "construct-only": false, + "controllable": false, + "mutable": "ready", + "readable": true, + "type": "GstValueArray", + "writable": true + }, + "video-codecs": { + "blurb": "Names of video codecs to be be used during the SDP negotiation. Valid values: [VP8, H264, VP9, H265]", + "conditionally-available": false, + "construct": false, + "construct-only": false, + "controllable": false, + "mutable": "ready", + "readable": true, + "type": "GstValueArray", + "writable": true + } + }, + "signals": { + "request-encoded-filter": { + "args": [ + { + "name": "arg0", + "type": "gchararray" + }, + { + "name": "arg1", + "type": "gchararray" + }, + { + "name": "arg2", + "type": "GstCaps" + } + ], + "return-type": "GstElement", + "when": "last" + } + } + }, "GstRSWebRTCSignallableIface": { "hierarchy": [ "GstRSWebRTCSignallableIface", diff --git a/net/webrtc/src/webrtcsrc/imp.rs b/net/webrtc/src/webrtcsrc/imp.rs index 8b27cecf..42a05108 100644 --- a/net/webrtc/src/webrtcsrc/imp.rs +++ b/net/webrtc/src/webrtcsrc/imp.rs @@ -43,7 +43,7 @@ struct Settings { } #[derive(Default)] -pub struct WebRTCSrc { +pub struct BaseWebRTCSrc { settings: Mutex, n_video_pads: AtomicU16, n_audio_pads: AtomicU16, @@ -51,14 +51,21 @@ pub struct WebRTCSrc { } #[glib::object_subclass] -impl ObjectSubclass for WebRTCSrc { - const NAME: &'static str = "GstWebRTCSrc"; - type Type = super::WebRTCSrc; +impl ObjectSubclass for BaseWebRTCSrc { + const NAME: &'static str = "GstBaseWebRTCSrc"; + type Type = super::BaseWebRTCSrc; type ParentType = gst::Bin; - type Interfaces = (gst::URIHandler, gst::ChildProxy); + type Interfaces = (gst::ChildProxy,); } -impl ObjectImpl for WebRTCSrc { +unsafe impl IsSubclassable for super::BaseWebRTCSrc { + fn class_init(class: &mut glib::Class) { + Self::parent_class_init::(class); + } +} +pub(crate) trait BaseWebRTCSrcImpl: BinImpl {} + +impl ObjectImpl for BaseWebRTCSrc { fn properties() -> &'static [glib::ParamSpec] { static PROPS: Lazy> = Lazy::new(|| { vec![ @@ -205,7 +212,7 @@ impl ObjectImpl for WebRTCSrc { static SIGNALS: Lazy> = Lazy::new(|| { vec![ /** - * WebRTCSrc::request-encoded-filter: + * BaseWebRTCSrc::request-encoded-filter: * @producer_id: Identifier of the producer * @pad_name: The name of the output pad * @allowed_caps: the allowed caps for the output pad @@ -278,7 +285,7 @@ struct SignallerSignals { handle_ice: glib::SignalHandlerId, } -impl WebRTCSrc { +impl BaseWebRTCSrc { fn webrtcbin(&self) -> gst::Bin { let state = self.state.lock().unwrap(); let webrtcbin = state @@ -562,15 +569,22 @@ impl WebRTCSrc { let mline = transceiver.map_or(mline, |t| Some(t.mlineindex())); // Same logic as gst_pad_create_stream_id and friends, making a hash of - // the URI and adding `:`, here the ID is the mline of the + // the URI (session id, if URI doesn't exist) and adding `:`, here the ID is the mline of the // stream in the SDP. mline.map(|mline| { let mut cs = glib::Checksum::new(glib::ChecksumType::Sha256).unwrap(); - cs.update( - self.uri() - .expect("get_stream_id should never be called if no URI has been set") - .as_bytes(), - ); + + let data: String = if self + .signaller() + .has_property("uri", Some(String::static_type())) + { + self.signaller().property::>("uri").unwrap() + } else { + // use the session id + self.state.lock().unwrap().session_id.clone().unwrap() + }; + + cs.update(data.as_bytes()); format!("{}:{mline}", cs.string().unwrap()) }) @@ -983,20 +997,7 @@ impl WebRTCSrc { } } -impl ElementImpl for WebRTCSrc { - fn metadata() -> Option<&'static gst::subclass::ElementMetadata> { - static ELEMENT_METADATA: Lazy = Lazy::new(|| { - gst::subclass::ElementMetadata::new( - "WebRTCSrc", - "Source/Network/WebRTC", - "WebRTC src", - "Thibault Saunier ", - ) - }); - - Some(&*ELEMENT_METADATA) - } - +impl ElementImpl for BaseWebRTCSrc { fn pad_templates() -> &'static [gst::PadTemplate] { static PAD_TEMPLATES: Lazy> = Lazy::new(|| { let mut video_caps_builder = gst::Caps::builder_full() @@ -1095,11 +1096,11 @@ impl ElementImpl for WebRTCSrc { } } -impl GstObjectImpl for WebRTCSrc {} +impl GstObjectImpl for BaseWebRTCSrc {} -impl BinImpl for WebRTCSrc {} +impl BinImpl for BaseWebRTCSrc {} -impl ChildProxyImpl for WebRTCSrc { +impl ChildProxyImpl for BaseWebRTCSrc { fn child_by_index(&self, index: u32) -> Option { if index == 0 { Some(self.signaller().upcast()) @@ -1123,42 +1124,6 @@ impl ChildProxyImpl for WebRTCSrc { } } -impl URIHandlerImpl for WebRTCSrc { - const URI_TYPE: gst::URIType = gst::URIType::Src; - - fn protocols() -> &'static [&'static str] { - &["gstwebrtc", "gstwebrtcs"] - } - - fn uri(&self) -> Option { - self.signaller().property::>("uri") - } - - fn set_uri(&self, uri: &str) -> Result<(), glib::Error> { - let uri = Url::from_str(uri) - .map_err(|err| glib::Error::new(gst::URIError::BadUri, &format!("{:?}", err)))?; - - let socket_scheme = match uri.scheme() { - "gstwebrtc" => Ok("ws"), - "gstwebrtcs" => Ok("wss"), - _ => Err(glib::Error::new( - gst::URIError::BadUri, - &format!("Invalid protocol: {}", uri.scheme()), - )), - }?; - - let mut url_str = uri.to_string(); - - // Not using `set_scheme()` because it doesn't work with `http` - // See https://github.com/servo/rust-url/pull/768 for a PR implementing that - url_str.replace_range(0..uri.scheme().len(), socket_scheme); - - self.signaller().set_property("uri", &url_str); - - Ok(()) - } -} - #[derive(PartialEq)] enum SignallerState { Started, @@ -1186,3 +1151,77 @@ impl Default for State { } } } + +#[derive(Default)] +pub struct WebRTCSrc {} + +impl ObjectImpl for WebRTCSrc {} + +impl GstObjectImpl for WebRTCSrc {} + +impl BinImpl for WebRTCSrc {} + +impl ElementImpl for WebRTCSrc { + fn metadata() -> Option<&'static gst::subclass::ElementMetadata> { + static ELEMENT_METADATA: Lazy = Lazy::new(|| { + gst::subclass::ElementMetadata::new( + "WebRTCSrc", + "Source/Network/WebRTC", + "WebRTC src", + "Thibault Saunier ", + ) + }); + + Some(&*ELEMENT_METADATA) + } +} + +impl BaseWebRTCSrcImpl for WebRTCSrc {} + +impl URIHandlerImpl for WebRTCSrc { + const URI_TYPE: gst::URIType = gst::URIType::Src; + + fn protocols() -> &'static [&'static str] { + &["gstwebrtc", "gstwebrtcs"] + } + + fn uri(&self) -> Option { + let obj = self.obj(); + let base = obj.upcast_ref::().imp(); + base.signaller().property::>("uri") + } + + fn set_uri(&self, uri: &str) -> Result<(), glib::Error> { + let uri = Url::from_str(uri) + .map_err(|err| glib::Error::new(gst::URIError::BadUri, &format!("{:?}", err)))?; + + let socket_scheme = match uri.scheme() { + "gstwebrtc" => Ok("ws"), + "gstwebrtcs" => Ok("wss"), + _ => Err(glib::Error::new( + gst::URIError::BadUri, + &format!("Invalid protocol: {}", uri.scheme()), + )), + }?; + + let mut url_str = uri.to_string(); + + // Not using `set_scheme()` because it doesn't work with `http` + // See https://github.com/servo/rust-url/pull/768 for a PR implementing that + url_str.replace_range(0..uri.scheme().len(), socket_scheme); + + let obj = self.obj(); + let base = obj.upcast_ref::().imp(); + base.signaller().set_property("uri", &url_str); + + Ok(()) + } +} + +#[glib::object_subclass] +impl ObjectSubclass for WebRTCSrc { + const NAME: &'static str = "GstWebRTCSrc"; + type Type = super::WebRTCSrc; + type ParentType = super::BaseWebRTCSrc; + type Interfaces = (gst::URIHandler,); +} diff --git a/net/webrtc/src/webrtcsrc/mod.rs b/net/webrtc/src/webrtcsrc/mod.rs index 72167d5d..b85612bc 100644 --- a/net/webrtc/src/webrtcsrc/mod.rs +++ b/net/webrtc/src/webrtcsrc/mod.rs @@ -42,7 +42,11 @@ use gst::prelude::*; use gst::{glib, prelude::StaticType}; glib::wrapper! { - pub struct WebRTCSrc(ObjectSubclass) @extends gst::Bin, gst::Element, gst::Object, @implements gst::URIHandler, gst::ChildProxy; + pub struct BaseWebRTCSrc(ObjectSubclass) @extends gst::Bin, gst::Element, gst::Object, @implements gst::ChildProxy; +} + +glib::wrapper! { + pub struct WebRTCSrc(ObjectSubclass) @extends BaseWebRTCSrc, gst::Bin, gst::Element, gst::Object, @implements gst::URIHandler, gst::ChildProxy; } glib::wrapper! { @@ -50,6 +54,7 @@ glib::wrapper! { } pub fn register(plugin: Option<&gst::Plugin>) -> Result<(), glib::BoolError> { + BaseWebRTCSrc::static_type().mark_as_plugin_api(gst::PluginAPIFlags::empty()); WebRTCSignallerRole::static_type().mark_as_plugin_api(gst::PluginAPIFlags::empty()); WebRTCSrcPad::static_type().mark_as_plugin_api(gst::PluginAPIFlags::empty()); Signallable::static_type().mark_as_plugin_api(gst::PluginAPIFlags::empty());