diff --git a/docs/plugins/gst_plugins_cache.json b/docs/plugins/gst_plugins_cache.json index 5e95a7dd..d87d4877 100644 --- a/docs/plugins/gst_plugins_cache.json +++ b/docs/plugins/gst_plugins_cache.json @@ -6704,12 +6704,14 @@ "audio_%%u": { "caps": "audio/x-raw:\naudio/x-opus:\n", "direction": "sink", - "presence": "request" + "presence": "request", + "type": "GstWebRTCSinkPad" }, "video_%%u": { "caps": "video/x-raw:\n\nvideo/x-raw(memory:CUDAMemory):\n\nvideo/x-raw(memory:GLMemory):\n\nvideo/x-raw(memory:NVMM):\n\nvideo/x-raw(memory:D3D11Memory):\nvideo/x-vp8:\nvideo/x-h264:\nvideo/x-vp9:\nvideo/x-h265:\n", "direction": "sink", - "presence": "request" + "presence": "request", + "type": "GstWebRTCSinkPad" } }, "rank": "none" @@ -6735,12 +6737,14 @@ "audio_%%u": { "caps": "audio/x-raw:\naudio/x-opus:\n", "direction": "sink", - "presence": "request" + "presence": "request", + "type": "GstWebRTCSinkPad" }, "video_%%u": { "caps": "video/x-raw:\n\nvideo/x-raw(memory:CUDAMemory):\n\nvideo/x-raw(memory:GLMemory):\n\nvideo/x-raw(memory:NVMM):\n\nvideo/x-raw(memory:D3D11Memory):\nvideo/x-vp8:\nvideo/x-h264:\nvideo/x-vp9:\nvideo/x-h265:\n", "direction": "sink", - "presence": "request" + "presence": "request", + "type": "GstWebRTCSinkPad" } }, "rank": "none" @@ -6766,12 +6770,14 @@ "audio_%%u": { "caps": "audio/x-raw:\naudio/x-opus:\n", "direction": "sink", - "presence": "request" + "presence": "request", + "type": "GstWebRTCSinkPad" }, "video_%%u": { "caps": "video/x-raw:\n\nvideo/x-raw(memory:CUDAMemory):\n\nvideo/x-raw(memory:GLMemory):\n\nvideo/x-raw(memory:NVMM):\n\nvideo/x-raw(memory:D3D11Memory):\nvideo/x-vp8:\nvideo/x-h264:\nvideo/x-vp9:\nvideo/x-h265:\n", "direction": "sink", - "presence": "request" + "presence": "request", + "type": "GstWebRTCSinkPad" } }, "rank": "none" @@ -6797,12 +6803,14 @@ "audio_%%u": { "caps": "audio/x-raw:\naudio/x-opus:\n", "direction": "sink", - "presence": "request" + "presence": "request", + "type": "GstWebRTCSinkPad" }, "video_%%u": { "caps": "video/x-raw:\n\nvideo/x-raw(memory:CUDAMemory):\n\nvideo/x-raw(memory:GLMemory):\n\nvideo/x-raw(memory:NVMM):\n\nvideo/x-raw(memory:D3D11Memory):\nvideo/x-vp8:\nvideo/x-h264:\nvideo/x-vp9:\nvideo/x-h265:\n", "direction": "sink", - "presence": "request" + "presence": "request", + "type": "GstWebRTCSinkPad" } }, "rank": "none" @@ -6862,12 +6870,14 @@ "audio_%%u": { "caps": "audio/x-raw:\naudio/x-opus:\n", "direction": "sink", - "presence": "request" + "presence": "request", + "type": "GstWebRTCSinkPad" }, "video_%%u": { "caps": "video/x-raw:\n\nvideo/x-raw(memory:CUDAMemory):\n\nvideo/x-raw(memory:GLMemory):\n\nvideo/x-raw(memory:NVMM):\n\nvideo/x-raw(memory:D3D11Memory):\nvideo/x-vp8:\nvideo/x-h264:\nvideo/x-vp9:\nvideo/x-h265:\n", "direction": "sink", - "presence": "request" + "presence": "request", + "type": "GstWebRTCSinkPad" } }, "rank": "none" @@ -7590,6 +7600,32 @@ } ] }, + "GstWebRTCSinkPad": { + "hierarchy": [ + "GstWebRTCSinkPad", + "GstGhostPad", + "GstProxyPad", + "GstPad", + "GstObject", + "GInitiallyUnowned", + "GObject" + ], + "kind": "object", + "properties": { + "msid": { + "blurb": "Remote MediaStream ID in use for this pad", + "conditionally-available": false, + "construct": false, + "construct-only": false, + "controllable": false, + "default": "NULL", + "mutable": "ready", + "readable": true, + "type": "gchararray", + "writable": true + } + } + }, "GstWebRTCSrcPad": { "hierarchy": [ "GstWebRTCSrcPad", diff --git a/net/webrtc/src/webrtcsink/imp.rs b/net/webrtc/src/webrtcsink/imp.rs index 54e41a96..ce23f742 100644 --- a/net/webrtc/src/webrtcsink/imp.rs +++ b/net/webrtc/src/webrtcsink/imp.rs @@ -20,7 +20,9 @@ use std::ops::Mul; use std::sync::{mpsc, Arc, Condvar, Mutex}; use super::homegrown_cc::CongestionController; -use super::{WebRTCSinkCongestionControl, WebRTCSinkError, WebRTCSinkMitigationMode}; +use super::{ + WebRTCSinkCongestionControl, WebRTCSinkError, WebRTCSinkMitigationMode, WebRTCSinkPad, +}; use crate::aws_kvs_signaller::AwsKvsSignaller; use crate::janusvr_signaller::JanusVRSignaller; use crate::livekit_signaller::LiveKitSignaller; @@ -186,7 +188,7 @@ impl futures::stream::FusedStream for CustomBusStream { /// Wrapper around our sink pads #[derive(Debug, Clone)] struct InputStream { - sink_pad: gst::GhostPad, + sink_pad: WebRTCSinkPad, producer: Option, /// The (fixed) caps coming in in_caps: Option, @@ -1434,6 +1436,10 @@ impl InputStream { ), ) } + + fn msid(&self) -> Option { + self.sink_pad.property("msid") + } } impl NavigationEventHandler { @@ -1738,6 +1744,11 @@ impl BaseWebRTCSink { return; }; + if let Some(msid) = stream.msid() { + gst::trace!(CAT, obj: element, "forwarding msid={msid:?} to webrtcbin sinkpad"); + pad.set_property("msid", &msid); + } + let transceiver = pad.property::("transceiver"); transceiver.set_property( @@ -4071,11 +4082,12 @@ impl ElementImpl for BaseWebRTCSink { caps_builder = caps_builder.structure(codec.caps.structure(0).unwrap().to_owned()); } - let video_pad_template = gst::PadTemplate::new( + let video_pad_template = gst::PadTemplate::with_gtype( "video_%u", gst::PadDirection::Sink, gst::PadPresence::Request, &caps_builder.build(), + WebRTCSinkPad::static_type(), ) .unwrap(); @@ -4084,11 +4096,12 @@ impl ElementImpl for BaseWebRTCSink { for codec in Codecs::audio_codecs() { caps_builder = caps_builder.structure(codec.caps.structure(0).unwrap().to_owned()); } - let audio_pad_template = gst::PadTemplate::new( + let audio_pad_template = gst::PadTemplate::with_gtype( "audio_%u", gst::PadDirection::Sink, gst::PadPresence::Request, &caps_builder.build(), + WebRTCSinkPad::static_type(), ) .unwrap(); @@ -4127,13 +4140,13 @@ impl ElementImpl for BaseWebRTCSink { (name, false) }; - let sink_pad = gst::GhostPad::builder_from_template(templ) + let sink_pad = gst::PadBuilder::::from_template(templ) .name(name.as_str()) .chain_function(|pad, parent, buffer| { BaseWebRTCSink::catch_panic_pad_function( parent, || Err(gst::FlowError::Error), - |this| this.chain(pad, buffer), + |this| this.chain(pad.upcast_ref(), buffer), ) }) .event_function(|pad, parent, event| { @@ -4250,7 +4263,7 @@ impl ChildProxyImpl for BaseWebRTCSink { fn child_by_name(&self, name: &str) -> Option { match name { "signaller" => Some(self.settings.lock().unwrap().signaller.clone().upcast()), - _ => None, + _ => self.obj().static_pad(name).map(|pad| pad.upcast()), } } } diff --git a/net/webrtc/src/webrtcsink/mod.rs b/net/webrtc/src/webrtcsink/mod.rs index 413d0220..f566868a 100644 --- a/net/webrtc/src/webrtcsink/mod.rs +++ b/net/webrtc/src/webrtcsink/mod.rs @@ -39,11 +39,16 @@ use gst::subclass::prelude::*; mod homegrown_cc; mod imp; +mod pad; glib::wrapper! { pub struct BaseWebRTCSink(ObjectSubclass) @extends gst::Bin, gst::Element, gst::Object, @implements gst::ChildProxy, gst_video::Navigation; } +glib::wrapper! { + pub struct WebRTCSinkPad(ObjectSubclass) @extends gst::GhostPad, gst::ProxyPad, gst::Pad, gst::Object; +} + glib::wrapper! { pub struct WebRTCSink(ObjectSubclass) @extends BaseWebRTCSink, gst::Bin, gst::Element, gst::Object, @implements gst::ChildProxy, gst_video::Navigation; } @@ -124,6 +129,7 @@ enum WebRTCSinkMitigationMode { } pub fn register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> { + WebRTCSinkPad::static_type().mark_as_plugin_api(gst::PluginAPIFlags::empty()); BaseWebRTCSink::static_type().mark_as_plugin_api(gst::PluginAPIFlags::empty()); WebRTCSinkCongestionControl::static_type().mark_as_plugin_api(gst::PluginAPIFlags::empty()); gst::Element::register( diff --git a/net/webrtc/src/webrtcsink/pad.rs b/net/webrtc/src/webrtcsink/pad.rs new file mode 100644 index 00000000..55d12ac7 --- /dev/null +++ b/net/webrtc/src/webrtcsink/pad.rs @@ -0,0 +1,57 @@ +// SPDX-License-Identifier: MPL-2.0 + +use gst::{glib, prelude::*, subclass::prelude::*}; +use once_cell::sync::Lazy; +use std::sync::Mutex; + +#[derive(Default)] +pub struct WebRTCSinkPad { + settings: Mutex, +} + +#[derive(Debug, Default)] +struct Settings { + msid: Option, +} + +#[glib::object_subclass] +impl ObjectSubclass for WebRTCSinkPad { + const NAME: &'static str = "GstWebRTCSinkPad"; + type Type = super::WebRTCSinkPad; + type ParentType = gst::GhostPad; +} + +impl ObjectImpl for WebRTCSinkPad { + fn properties() -> &'static [glib::ParamSpec] { + static PROPS: Lazy> = Lazy::new(|| { + vec![glib::ParamSpecString::builder("msid") + .flags(glib::ParamFlags::READWRITE | gst::PARAM_FLAG_MUTABLE_READY) + .blurb("Remote MediaStream ID in use for this pad") + .build()] + }); + PROPS.as_ref() + } + fn set_property(&self, _id: usize, value: &glib::Value, pspec: &glib::ParamSpec) { + let mut settings = self.settings.lock().unwrap(); + match pspec.name() { + "msid" => { + settings.msid = value + .get::>() + .expect("type checked upstream") + } + name => panic!("no writable property {name:?}"), + } + } + fn property(&self, _id: usize, pspec: &glib::ParamSpec) -> glib::Value { + let settings = self.settings.lock().unwrap(); + match pspec.name() { + "msid" => settings.msid.to_value(), + name => panic!("no readable property {name:?}"), + } + } +} + +impl GstObjectImpl for WebRTCSinkPad {} +impl PadImpl for WebRTCSinkPad {} +impl ProxyPadImpl for WebRTCSinkPad {} +impl GhostPadImpl for WebRTCSinkPad {}