From 027eead86d1a11e64cbb680d737e0ec200cabc29 Mon Sep 17 00:00:00 2001 From: Guillaume Desmottes Date: Tue, 19 Mar 2024 16:41:09 +0100 Subject: [PATCH] webrtc: janus: add 'janus-state' property to the sink This property can be used by applications to track the state of the signaller, especially to know when the stream is up. Fix #510 Part-of: --- docs/plugins/gst_plugins_cache.json | 47 +++++++++++++++++++++ net/webrtc/src/janusvr_signaller/imp.rs | 56 ++++++++++++++++++++++--- net/webrtc/src/webrtcsink/imp.rs | 50 +++++++++++++++++++--- net/webrtc/src/webrtcsink/mod.rs | 23 ++++++++++ 4 files changed, 166 insertions(+), 10 deletions(-) diff --git a/docs/plugins/gst_plugins_cache.json b/docs/plugins/gst_plugins_cache.json index 146c6d5a..89e64ce6 100644 --- a/docs/plugins/gst_plugins_cache.json +++ b/docs/plugins/gst_plugins_cache.json @@ -10126,6 +10126,18 @@ } }, "properties": { + "janus-state": { + "blurb": "The current state of the signaller", + "conditionally-available": false, + "construct": false, + "construct-only": false, + "controllable": false, + "default": "initialized (0)", + "mutable": "null", + "readable": true, + "type": "GstJanusVRWebRTCJanusState", + "writable": false + }, "use-string-ids": { "blurb": "Use strings instead of u64 for Janus IDs, see strings_ids config option in janus.plugin.videoroom.jcfg", "conditionally-available": false, @@ -11011,6 +11023,41 @@ } } }, + "GstJanusVRWebRTCJanusState": { + "kind": "enum", + "values": [ + { + "desc": "Initialized", + "name": "initialized", + "value": "0" + }, + { + "desc": "SessionCreated", + "name": "session-created", + "value": "1" + }, + { + "desc": "VideoroomAttached", + "name": "videoroom-attached", + "value": "2" + }, + { + "desc": "RoomJoined", + "name": "room-joined", + "value": "3" + }, + { + "desc": "Negotiating", + "name": "negotiating", + "value": "4" + }, + { + "desc": "WebrtcUp", + "name": "webrtc-up", + "value": "5" + } + ] + }, "GstLiveKitWebRTCSrcPad": { "hierarchy": [ "GstLiveKitWebRTCSrcPad", diff --git a/net/webrtc/src/janusvr_signaller/imp.rs b/net/webrtc/src/janusvr_signaller/imp.rs index db0b7e3c..bbe07fd5 100644 --- a/net/webrtc/src/janusvr_signaller/imp.rs +++ b/net/webrtc/src/janusvr_signaller/imp.rs @@ -1,7 +1,10 @@ // SPDX-License-Identifier: MPL-2.0 -use crate::signaller::{Signallable, SignallableImpl}; -use crate::RUNTIME; +use crate::{ + signaller::{Signallable, SignallableImpl}, + webrtcsink::JanusVRSignallerState, + RUNTIME, +}; use anyhow::{anyhow, Error}; use async_tungstenite::tungstenite; @@ -439,6 +442,9 @@ impl Signaller { match reply { JsonReply::WebRTCUp => { gst::trace!(CAT, imp = self, "WebRTC streaming is working!"); + + self.obj() + .emit_by_name::<()>("state-updated", &[&JanusVRSignallerState::WebrtcUp]); } JsonReply::Success(success) => { if let Some(data) = success.data { @@ -491,6 +497,12 @@ impl Signaller { "Joined room {:?} successfully", joined.room ); + + self.obj().emit_by_name::<()>( + "state-updated", + &[&JanusVRSignallerState::RoomJoined], + ); + self.session_requested(); } VideoRoomData::Event(room_event) => { @@ -587,11 +599,23 @@ impl Signaller { } fn set_session_id(&self, session_id: u64) { - self.state.lock().unwrap().session_id = Some(session_id); + { + let mut state = self.state.lock().unwrap(); + state.session_id = Some(session_id); + } + self.obj() + .emit_by_name::<()>("state-updated", &[&JanusVRSignallerState::SessionCreated]); } fn set_handle_id(&self, handle_id: u64) { - self.state.lock().unwrap().handle_id = Some(handle_id); + { + let mut state = self.state.lock().unwrap(); + state.handle_id = Some(handle_id); + } + self.obj().emit_by_name::<()>( + "state-updated", + &[&JanusVRSignallerState::VideoroomAttached], + ); } fn attach_plugin(&self) { @@ -698,6 +722,9 @@ impl Signaller { return; } + self.obj() + .emit_by_name::<()>("state-updated", &[&JanusVRSignallerState::Negotiating]); + ( state.transaction_id.clone().unwrap(), state.session_id.unwrap(), @@ -867,7 +894,26 @@ impl ObjectSubclass for Signaller { } #[glib::derived_properties] -impl ObjectImpl for Signaller {} +impl ObjectImpl for Signaller { + fn signals() -> &'static [glib::subclass::Signal] { + static SIGNALS: Lazy> = Lazy::new(|| { + vec![ + /** + * GstJanusVRWebRTCSignaller::state-updated: + * @state: the new state. + * + * This signal is emitted when the Janus state of the signaller is updated. + */ + glib::subclass::Signal::builder("state-updated") + .param_types([JanusVRSignallerState::static_type()]) + .build(), + + ] + }); + + SIGNALS.as_ref() + } +} // below are Signaller subclasses implementing properties whose type depends of the Janus ID format (u64 or string). // User can control which signaller is used by setting the `use-string-ids` construct property on `janusvrwebrtcsink`. diff --git a/net/webrtc/src/webrtcsink/imp.rs b/net/webrtc/src/webrtcsink/imp.rs index 42f19f29..abfb050f 100644 --- a/net/webrtc/src/webrtcsink/imp.rs +++ b/net/webrtc/src/webrtcsink/imp.rs @@ -5918,13 +5918,21 @@ pub(super) mod livekit { #[cfg(feature = "janus")] pub(super) mod janus { use super::*; - use crate::janusvr_signaller::{JanusVRSignallerStr, JanusVRSignallerU64}; + use crate::{ + janusvr_signaller::{JanusVRSignallerStr, JanusVRSignallerU64}, + webrtcsink::JanusVRSignallerState, + }; #[derive(Debug, Clone, Default)] struct JanusSettings { use_string_ids: bool, } + #[derive(Debug, Default)] + struct JanusState { + janus_state: JanusVRSignallerState, + } + #[derive(Default, glib::Properties)] #[properties(wrapper_type = crate::webrtcsink::JanusVRWebRTCSink)] pub struct JanusVRWebRTCSink { @@ -5940,6 +5948,21 @@ pub(super) mod janus { */ #[property(name="use-string-ids", get, construct_only, type = bool, member = use_string_ids, blurb = "Use strings instead of u64 for Janus IDs, see strings_ids config option in janus.plugin.videoroom.jcfg")] settings: Mutex, + /** + * GstJanusVRWebRTCSink:janus-state: + * + * The current state of the signaller. + * Since: plugins-rs-0.14.0 + */ + #[property( + name = "janus-state", + // FIXME: can't use `member =` with enums: https://github.com/gtk-rs/gtk-rs-core/issues/1338 + get = |self_: &Self| self_.state.lock().unwrap().janus_state, + type = JanusVRSignallerState, + blurb = "The current state of the signaller", + builder(JanusVRSignallerState::Initialized) + )] + state: Mutex, } #[glib::derived_properties] @@ -5951,11 +5974,28 @@ pub(super) mod janus { .upcast_ref::() .imp(); - if settings.use_string_ids { - let _ = ws.set_signaller(JanusVRSignallerStr::default().upcast()); + let signaller: Signallable = if settings.use_string_ids { + JanusVRSignallerStr::default().upcast() } else { - let _ = ws.set_signaller(JanusVRSignallerU64::default().upcast()); - } + JanusVRSignallerU64::default().upcast() + }; + + let self_weak = self.downgrade(); + signaller.connect("state-updated", false, move |args| { + let self_ = self_weak.upgrade()?; + let janus_state = args[1].get::().unwrap(); + + { + let mut state = self_.state.lock().unwrap(); + state.janus_state = janus_state; + } + + self_.obj().notify("janus-state"); + + None + }); + + let _ = ws.set_signaller(signaller); } } diff --git a/net/webrtc/src/webrtcsink/mod.rs b/net/webrtc/src/webrtcsink/mod.rs index 33399467..e2c0128e 100644 --- a/net/webrtc/src/webrtcsink/mod.rs +++ b/net/webrtc/src/webrtcsink/mod.rs @@ -134,6 +134,26 @@ enum WebRTCSinkMitigationMode { DOWNSAMPLED = 0b00000010, } +#[derive(Debug, Default, Eq, PartialEq, Ord, PartialOrd, Hash, Clone, Copy, glib::Enum)] +#[repr(u32)] +#[enum_type(name = "GstJanusVRWebRTCJanusState")] +/// State of the Janus Signaller. +pub enum JanusVRSignallerState { + #[default] + /// Initial state when the signaller is created. + Initialized, + /// The Janus session has been created. + SessionCreated, + /// The session has been attached to the videoroom plugin. + VideoroomAttached, + /// The room has been joined. + RoomJoined, + /// The WebRTC stream is being negotiated. + Negotiating, + /// The WebRTC stream is streaming to Janus. + WebrtcUp, +} + 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()); @@ -230,5 +250,8 @@ pub fn register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> { JanusVRWebRTCSink::static_type(), )?; + #[cfg(feature = "janus")] + JanusVRSignallerState::static_type().mark_as_plugin_api(gst::PluginAPIFlags::empty()); + Ok(()) }