diff --git a/Cargo.lock b/Cargo.lock index 6779987c..e8e381c8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1002,6 +1002,8 @@ dependencies = [ "livekit-protocol", "log", "parking_lot", + "serde", + "serde_json", "thiserror", "tokio", "tokio-stream", diff --git a/examples/Cargo.lock b/examples/Cargo.lock index 89e39b32..fdf9c096 100644 --- a/examples/Cargo.lock +++ b/examples/Cargo.lock @@ -1758,6 +1758,8 @@ dependencies = [ "livekit-protocol", "log", "parking_lot", + "serde", + "serde_json", "thiserror", "tokio", "tokio-stream", diff --git a/examples/wgpu_room/src/app.rs b/examples/wgpu_room/src/app.rs index 1d6ea0c1..9a251f93 100644 --- a/examples/wgpu_room/src/app.rs +++ b/examples/wgpu_room/src/app.rs @@ -160,6 +160,12 @@ impl LkApp { let _ = self.service.send(AsyncCmd::ToggleSine); } }); + + ui.menu_button("Debug", |ui| { + if ui.button("Refresh stats").clicked() { + // TODO + } + }); }); } diff --git a/libwebrtc/Cargo.toml b/libwebrtc/Cargo.toml index 867a7488..f68ba88f 100644 --- a/libwebrtc/Cargo.toml +++ b/libwebrtc/Cargo.toml @@ -10,6 +10,8 @@ repository = "https://github.com/livekit/client-sdk-rust" [dependencies] livekit-protocol = { path = "../livekit-protocol", version = "0.2.0" } log = "0.4" +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" thiserror = "1.0" [target.'cfg(target_os = "android")'.dependencies] diff --git a/libwebrtc/src/data_channel.rs b/libwebrtc/src/data_channel.rs index 0ff57cae..49ca4db9 100644 --- a/libwebrtc/src/data_channel.rs +++ b/libwebrtc/src/data_channel.rs @@ -13,6 +13,7 @@ // limitations under the License. use crate::{imp::data_channel as dc_imp, rtp_parameters::Priority}; +use serde::Deserialize; use std::{fmt::Debug, str::Utf8Error}; use thiserror::Error; @@ -49,8 +50,9 @@ pub enum DataChannelError { Utf8(#[from] Utf8Error), } -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub enum DataState { +#[derive(Debug, Copy, Clone, PartialEq, Eq, Deserialize)] +#[serde(rename_all = "lowercase")] +pub enum DataChannelState { Connecting, Open, Closing, @@ -63,7 +65,7 @@ pub struct DataBuffer<'a> { pub binary: bool, } -pub type OnStateChange = Box; +pub type OnStateChange = Box; pub type OnMessage = Box; pub type OnBufferedAmountChange = Box; @@ -85,7 +87,7 @@ impl DataChannel { self.handle.label() } - pub fn state(&self) -> DataState { + pub fn state(&self) -> DataChannelState { self.handle.state() } diff --git a/libwebrtc/src/lib.rs b/libwebrtc/src/lib.rs index bb097b79..4e954f52 100644 --- a/libwebrtc/src/lib.rs +++ b/libwebrtc/src/lib.rs @@ -56,6 +56,7 @@ pub mod rtp_receiver; pub mod rtp_sender; pub mod rtp_transceiver; pub mod session_description; +pub mod stats; pub mod video_frame; pub mod video_source; pub mod video_stream; diff --git a/libwebrtc/src/native/data_channel.rs b/libwebrtc/src/native/data_channel.rs index eb77bec2..4f7b0f21 100644 --- a/libwebrtc/src/native/data_channel.rs +++ b/libwebrtc/src/native/data_channel.rs @@ -13,8 +13,8 @@ // limitations under the License. use crate::data_channel::{ - DataBuffer, DataChannelError, DataChannelInit, DataState, OnBufferedAmountChange, OnMessage, - OnStateChange, + DataBuffer, DataChannelError, DataChannelInit, DataChannelState, OnBufferedAmountChange, + OnMessage, OnStateChange, }; use cxx::SharedPtr; use parking_lot::Mutex; @@ -22,7 +22,7 @@ use std::str; use std::sync::Arc; use webrtc_sys::data_channel as sys_dc; -impl From for DataState { +impl From for DataChannelState { fn from(value: sys_dc::ffi::DataState) -> Self { match value { sys_dc::ffi::DataState::Connecting => Self::Connecting, @@ -95,7 +95,7 @@ impl DataChannel { self.sys_handle.label() } - pub fn state(&self) -> DataState { + pub fn state(&self) -> DataChannelState { self.sys_handle.state().into() } diff --git a/libwebrtc/src/native/peer_connection.rs b/libwebrtc/src/native/peer_connection.rs index 66a24fef..9c751dad 100644 --- a/libwebrtc/src/native/peer_connection.rs +++ b/libwebrtc/src/native/peer_connection.rs @@ -38,6 +38,7 @@ use crate::rtp_receiver::RtpReceiver; use crate::rtp_sender::RtpSender; use crate::rtp_transceiver::RtpTransceiver; use crate::rtp_transceiver::RtpTransceiverInit; +use crate::stats::RtcStats; use crate::MediaType; use crate::RtcErrorType; use crate::{session_description::SessionDescription, RtcError}; @@ -49,6 +50,7 @@ use tokio::sync::oneshot; use webrtc_sys::data_channel as sys_dc; use webrtc_sys::jsep as sys_jsep; use webrtc_sys::peer_connection as sys_pc; +use webrtc_sys::peer_connection_factory as sys_pcf; use webrtc_sys::rtc_error as sys_err; impl From for sys_pc::ffi::RtcOfferAnswerOptions { @@ -203,7 +205,7 @@ impl PeerConnection { options: OfferOptions, ) -> Result { let (tx, mut rx) = mpsc::channel::>(1); - let ctx = Box::new(sys_pc::AsyncContext(Box::new(tx))); + let ctx = Box::new(sys_pc::PeerContext(Box::new(tx))); type CtxType = mpsc::Sender>; self.sys_handle.create_offer( @@ -229,7 +231,7 @@ impl PeerConnection { options: AnswerOptions, ) -> Result { let (tx, mut rx) = mpsc::channel::>(1); - let ctx = Box::new(sys_pc::AsyncContext(Box::new(tx))); + let ctx = Box::new(sys_pc::PeerContext(Box::new(tx))); type CtxType = mpsc::Sender>; self.sys_handle.create_answer( @@ -252,7 +254,7 @@ impl PeerConnection { pub async fn set_local_description(&self, desc: SessionDescription) -> Result<(), RtcError> { let (tx, rx) = oneshot::channel::>(); - let ctx = Box::new(sys_pc::AsyncContext(Box::new(tx))); + let ctx = Box::new(sys_pc::PeerContext(Box::new(tx))); self.sys_handle .set_local_description(desc.handle.sys_handle, ctx, |ctx, err| { @@ -273,7 +275,7 @@ impl PeerConnection { pub async fn set_remote_description(&self, desc: SessionDescription) -> Result<(), RtcError> { let (tx, rx) = oneshot::channel::>(); - let ctx = Box::new(sys_pc::AsyncContext(Box::new(tx))); + let ctx = Box::new(sys_pc::PeerContext(Box::new(tx))); self.sys_handle .set_remote_description(desc.handle.sys_handle, ctx, |ctx, err| { @@ -297,7 +299,7 @@ impl PeerConnection { pub async fn add_ice_candidate(&self, candidate: IceCandidate) -> Result<(), RtcError> { let (tx, rx) = oneshot::channel::>(); - let ctx = Box::new(sys_pc::AsyncContext(Box::new(tx))); + let ctx = Box::new(sys_pc::PeerContext(Box::new(tx))); self.sys_handle .add_ice_candidate(candidate.handle.sys_handle, ctx, |ctx, err| { @@ -440,6 +442,27 @@ impl PeerConnection { .map_err(|e| unsafe { sys_err::ffi::RtcError::from(e.what()).into() }) } + pub async fn get_stats(&self) -> Result, RtcError> { + let (tx, rx) = oneshot::channel::, RtcError>>(); + let ctx = Box::new(sys_pc::PeerContext(Box::new(tx))); + + self.sys_handle.get_stats(ctx, |ctx, stats| { + let tx = ctx + .0 + .downcast::, RtcError>>>() + .unwrap(); + + // Unwrap because it should not happens + let vec = serde_json::from_str(&stats).unwrap(); + let _ = tx.send(Ok(vec)); + }); + + rx.await.map_err(|_| RtcError { + error_type: RtcErrorType::Internal, + message: "get_stats cancelled".to_owned(), + })? + } + pub fn senders(&self) -> Vec { self.sys_handle .get_senders() @@ -526,7 +549,7 @@ pub struct PeerObserver { pub track_handler: Mutex>, } -impl sys_pc::PeerConnectionObserver for PeerObserver { +impl sys_pcf::PeerConnectionObserver for PeerObserver { fn on_signaling_change(&self, new_state: sys_pc::ffi::SignalingState) { if let Some(f) = self.signaling_change_handler.lock().as_mut() { f(new_state.into()); @@ -612,7 +635,7 @@ impl sys_pc::PeerConnectionObserver for PeerObserver { fn on_ice_selected_candidate_pair_changed( &self, - _event: sys_pc::ffi::CandidatePairChangeEvent, + _event: sys_pcf::ffi::CandidatePairChangeEvent, ) { } diff --git a/libwebrtc/src/native/peer_connection_factory.rs b/libwebrtc/src/native/peer_connection_factory.rs index a9c5db71..12da694c 100644 --- a/libwebrtc/src/native/peer_connection_factory.rs +++ b/libwebrtc/src/native/peer_connection_factory.rs @@ -29,7 +29,6 @@ use cxx::UniquePtr; use lazy_static::lazy_static; use parking_lot::Mutex; use std::sync::Arc; -use webrtc_sys::peer_connection as sys_pc; use webrtc_sys::peer_connection_factory as sys_pcf; use webrtc_sys::rtc_error as sys_err; use webrtc_sys::webrtc as sys_rtc; @@ -78,13 +77,12 @@ impl PeerConnectionFactory { config: RtcConfiguration, ) -> Result { let observer = Arc::new(imp_pc::PeerObserver::default()); - let native_observer = sys_pc::ffi::create_native_peer_connection_observer(Box::new( - sys_pc::PeerConnectionObserverWrapper::new(observer.clone()), - )); - - let res = self - .sys_handle - .create_peer_connection(config.into(), native_observer); + let res = self.sys_handle.create_peer_connection( + config.into(), + Box::new(sys_pcf::PeerConnectionObserverWrapper::new( + observer.clone(), + )), + ); match res { Ok(sys_handle) => Ok(PeerConnection { diff --git a/libwebrtc/src/native/rtp_receiver.rs b/libwebrtc/src/native/rtp_receiver.rs index b0fe64bc..ce59e92b 100644 --- a/libwebrtc/src/native/rtp_receiver.rs +++ b/libwebrtc/src/native/rtp_receiver.rs @@ -15,7 +15,10 @@ use crate::imp::media_stream_track::new_media_stream_track; use crate::media_stream_track::MediaStreamTrack; use crate::rtp_parameters::RtpParameters; +use crate::stats::RtcStats; +use crate::{RtcError, RtcErrorType}; use cxx::SharedPtr; +use tokio::sync::oneshot; use webrtc_sys::rtp_receiver as sys_rr; #[derive(Clone)] @@ -33,6 +36,27 @@ impl RtpReceiver { Some(new_media_stream_track(track_handle)) } + pub async fn get_stats(&self) -> Result, RtcError> { + let (tx, rx) = oneshot::channel::, RtcError>>(); + let ctx = Box::new(sys_rr::ReceiverContext(Box::new(tx))); + + self.sys_handle.get_stats(ctx, |ctx, stats| { + let tx = ctx + .0 + .downcast::, RtcError>>>() + .unwrap(); + + // Unwrap because it should not happens + let vec = serde_json::from_str(&stats).unwrap(); + let _ = tx.send(Ok(vec)); + }); + + rx.await.map_err(|_| RtcError { + error_type: RtcErrorType::Internal, + message: "get_stats cancelled".to_owned(), + })? + } + pub fn parameters(&self) -> RtpParameters { self.sys_handle.get_parameters().into() } diff --git a/libwebrtc/src/native/rtp_sender.rs b/libwebrtc/src/native/rtp_sender.rs index 1af9537d..fcb7b06f 100644 --- a/libwebrtc/src/native/rtp_sender.rs +++ b/libwebrtc/src/native/rtp_sender.rs @@ -14,8 +14,10 @@ use super::media_stream_track::new_media_stream_track; use crate::media_stream_track::MediaStreamTrack; +use crate::stats::RtcStats; use crate::{rtp_parameters::RtpParameters, RtcError, RtcErrorType}; use cxx::SharedPtr; +use tokio::sync::oneshot; use webrtc_sys::rtc_error as sys_err; use webrtc_sys::rtp_sender as sys_rs; @@ -34,6 +36,27 @@ impl RtpSender { Some(new_media_stream_track(track_handle)) } + pub async fn get_stats(&self) -> Result, RtcError> { + let (tx, rx) = oneshot::channel::, RtcError>>(); + let ctx = Box::new(sys_rs::SenderContext(Box::new(tx))); + + self.sys_handle.get_stats(ctx, |ctx, stats| { + let tx = ctx + .0 + .downcast::, RtcError>>>() + .unwrap(); + + // Unwrap because it should not happens + let vec = serde_json::from_str(&stats).unwrap(); + let _ = tx.send(Ok(vec)); + }); + + rx.await.map_err(|_| RtcError { + error_type: RtcErrorType::Internal, + message: "get_stats cancelled".to_owned(), + })? + } + pub fn set_track(&self, track: Option) -> Result<(), RtcError> { if !self .sys_handle diff --git a/libwebrtc/src/peer_connection.rs b/libwebrtc/src/peer_connection.rs index 648010c0..15ba3d2e 100644 --- a/libwebrtc/src/peer_connection.rs +++ b/libwebrtc/src/peer_connection.rs @@ -22,6 +22,7 @@ use crate::rtp_receiver::RtpReceiver; use crate::rtp_sender::RtpSender; use crate::rtp_transceiver::{RtpTransceiver, RtpTransceiverInit}; use crate::session_description::SessionDescription; +use crate::stats::RtcStats; use crate::{MediaType, RtcError}; use std::fmt::Debug; @@ -157,6 +158,10 @@ impl PeerConnection { self.handle.remove_track(sender) } + pub async fn get_stats(&self) -> Result, RtcError> { + self.handle.get_stats().await + } + pub fn add_transceiver( &self, track: MediaStreamTrack, @@ -172,6 +177,7 @@ impl PeerConnection { ) -> Result { self.handle.add_transceiver_for_media(media_type, init) } + pub fn close(&self) { self.handle.close() } diff --git a/libwebrtc/src/prelude.rs b/libwebrtc/src/prelude.rs index 9b606daa..0d9d695b 100644 --- a/libwebrtc/src/prelude.rs +++ b/libwebrtc/src/prelude.rs @@ -16,7 +16,7 @@ pub use crate::audio_frame::AudioFrame; pub use crate::audio_source::{AudioSourceOptions, RtcAudioSource}; pub use crate::audio_track::RtcAudioTrack; pub use crate::data_channel::{ - DataBuffer, DataChannel, DataChannelError, DataChannelInit, DataState, + DataBuffer, DataChannel, DataChannelError, DataChannelInit, DataChannelState, }; pub use crate::ice_candidate::IceCandidate; pub use crate::media_stream::MediaStream; diff --git a/libwebrtc/src/rtp_receiver.rs b/libwebrtc/src/rtp_receiver.rs index 765b329e..e8b297ff 100644 --- a/libwebrtc/src/rtp_receiver.rs +++ b/libwebrtc/src/rtp_receiver.rs @@ -16,7 +16,7 @@ use std::fmt::Debug; use crate::{ imp::rtp_receiver as imp_rr, media_stream_track::MediaStreamTrack, - rtp_parameters::RtpParameters, + rtp_parameters::RtpParameters, stats::RtcStats, RtcError, }; #[derive(Clone)] @@ -29,6 +29,10 @@ impl RtpReceiver { self.handle.track() } + pub async fn get_stats(&self) -> Result, RtcError> { + self.handle.get_stats().await + } + pub fn parameters(&self) -> RtpParameters { self.handle.parameters() } diff --git a/libwebrtc/src/rtp_sender.rs b/libwebrtc/src/rtp_sender.rs index 300c6f87..3301e253 100644 --- a/libwebrtc/src/rtp_sender.rs +++ b/libwebrtc/src/rtp_sender.rs @@ -16,7 +16,7 @@ use std::fmt::Debug; use crate::{ imp::rtp_sender as imp_rs, media_stream_track::MediaStreamTrack, rtp_parameters::RtpParameters, - RtcError, + stats::RtcStats, RtcError, }; #[derive(Clone)] @@ -29,6 +29,10 @@ impl RtpSender { self.handle.track() } + pub async fn get_stats(&self) -> Result, RtcError> { + self.handle.get_stats().await + } + pub fn set_track(&self, track: Option) -> Result<(), RtcError> { self.handle.set_track(track) } diff --git a/libwebrtc/src/stats.rs b/libwebrtc/src/stats.rs new file mode 100644 index 00000000..4e543f37 --- /dev/null +++ b/libwebrtc/src/stats.rs @@ -0,0 +1,589 @@ +use crate::data_channel::DataChannelState; +use serde::Deserialize; +use std::collections::HashMap; + +/// Values from https://www.w3.org/TR/webrtc-stats/ (NOTE: Some of the structs are not in the SPEC +/// but inside libwebrtc) +/// serde will handle the magic of correctly deserializing the json into our structs. +/// The enums values are inside encapsulated inside option because we're not sure about their default values (So we +/// default to None instead of an arbitrary value) + +#[derive(Debug, Clone, Deserialize)] +#[serde(tag = "type")] +#[serde(rename_all = "kebab-case")] +pub enum RtcStats { + Codec(CodecStats), + InboundRtp(InboundRtpStats), + OutboundRtp(OutboundRtpStats), + RemoteInboundRtp(RemoteInboundRtpStats), + RemoteOutboundRtp(RemoteOutboundRtpStats), + MediaSource(MediaSourceStats), + MediaPlayout(MediaPlayoutStats), + PeerConnection(PeerConnectionStats), + DataChannel(DataChannelStats), + Transport(TransportStats), + CandidatePair(CandidatePairStats), + LocalCandidate(LocalCandidateStats), + RemoteCandidate(RemoteCandidateStats), + Certificate(CertificateStats), + Track, // Deprecated +} + +#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, Deserialize)] +#[serde(rename_all = "lowercase")] +pub enum QualityLimitationReason { + #[default] + None, + Cpu, + Bandwidth, + Other, +} + +#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, Deserialize)] +#[serde(rename_all = "lowercase")] +pub enum IceRole { + #[default] + Unknown, + Controlling, + Controlled, +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq, Deserialize)] +#[serde(rename_all = "lowercase")] +pub enum DtlsTransportState { + New, + Connecting, + Connected, + Closed, + Failed, +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq, Deserialize)] +#[serde(rename_all = "lowercase")] +pub enum IceTransportState { + New, + Checking, + Connected, + Completed, + Disconnected, + Failed, + Closed, +} + +#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, Deserialize)] +#[serde(rename_all = "lowercase")] +pub enum DtlsRole { + Client, + Server, + #[default] + Unknown, +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq, Deserialize)] +#[serde(rename_all = "kebab-case")] +pub enum IceCandidatePairState { + Frozen, + Waiting, + InProgress, // in-progress + Failed, + Succeeded, +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq, Deserialize)] +#[serde(rename_all = "lowercase")] +pub enum IceCandidateType { + Host, + Srflx, + Prflx, + Relay, +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq, Deserialize)] +#[serde(rename_all = "lowercase")] +pub enum IceServerTransportProtocol { + Udp, + Tcp, + Tls, +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq, Deserialize)] +#[serde(rename_all = "lowercase")] +pub enum IceTcpCandidateType { + Active, + Passive, + So, +} + +#[derive(Debug, Default, Clone, Deserialize)] +pub struct CodecStats { + #[serde(flatten)] + pub rtc: dictionaries::RtcStats, + + #[serde(flatten)] + pub codec: dictionaries::CodecStats, +} + +#[derive(Debug, Default, Clone, Deserialize)] +pub struct InboundRtpStats { + #[serde(flatten)] + pub rtc: dictionaries::RtcStats, + + #[serde(flatten)] + pub stream: dictionaries::RtpStreamStats, + + #[serde(flatten)] + pub received: dictionaries::ReceivedRtpStreamStats, + + #[serde(flatten)] + pub inbound: dictionaries::InboundRtpStreamStats, +} + +#[derive(Debug, Default, Clone, Deserialize)] +pub struct OutboundRtpStats { + #[serde(flatten)] + pub rtc: dictionaries::RtcStats, + + #[serde(flatten)] + pub stream: dictionaries::RtpStreamStats, + + #[serde(flatten)] + pub sent: dictionaries::SentRtpStreamStats, + + #[serde(flatten)] + pub outbound: dictionaries::OutboundRtpStreamStats, +} + +#[derive(Debug, Default, Clone, Deserialize)] +pub struct RemoteInboundRtpStats { + #[serde(flatten)] + pub rtc: dictionaries::RtcStats, + + #[serde(flatten)] + pub stream: dictionaries::RtpStreamStats, + + #[serde(flatten)] + pub received: dictionaries::ReceivedRtpStreamStats, + + #[serde(flatten)] + pub remote_inbound: dictionaries::RemoteInboundRtpStreamStats, +} + +#[derive(Debug, Default, Clone, Deserialize)] +pub struct RemoteOutboundRtpStats { + #[serde(flatten)] + pub rtc: dictionaries::RtcStats, + + #[serde(flatten)] + pub stream: dictionaries::RtpStreamStats, + + #[serde(flatten)] + pub sent: dictionaries::SentRtpStreamStats, + + #[serde(flatten)] + pub remote_outbound: dictionaries::RemoteOutboundRtpStreamStats, +} + +#[derive(Debug, Default, Clone, Deserialize)] +pub struct MediaSourceStats { + #[serde(flatten)] + pub rtc: dictionaries::RtcStats, + + #[serde(flatten)] + pub source: dictionaries::MediaSourceStats, + + #[serde(flatten)] + pub audio: dictionaries::AudioSourceStats, + + #[serde(flatten)] + pub video: dictionaries::VideoSourceStats, +} + +#[derive(Debug, Default, Clone, Deserialize)] +pub struct MediaPlayoutStats { + #[serde(flatten)] + pub rtc: dictionaries::RtcStats, + + #[serde(flatten)] + pub audio_playout: dictionaries::AudioPlayoutStats, +} + +#[derive(Debug, Default, Clone, Deserialize)] +pub struct PeerConnectionStats { + #[serde(flatten)] + pub rtc: dictionaries::RtcStats, + + #[serde(flatten)] + pub pc: dictionaries::PeerConnectionStats, +} + +#[derive(Debug, Default, Clone, Deserialize)] +pub struct DataChannelStats { + #[serde(flatten)] + pub rtc: dictionaries::RtcStats, + + #[serde(flatten)] + pub dc: dictionaries::DataChannelStats, +} + +#[derive(Debug, Default, Clone, Deserialize)] +pub struct TransportStats { + #[serde(flatten)] + pub rtc: dictionaries::RtcStats, + + #[serde(flatten)] + pub transport: dictionaries::TransportStats, +} + +#[derive(Debug, Default, Clone, Deserialize)] +pub struct CandidatePairStats { + #[serde(flatten)] + pub rtc: dictionaries::RtcStats, + + #[serde(flatten)] + pub candidate_pair: dictionaries::CandidatePairStats, +} + +#[derive(Debug, Default, Clone, Deserialize)] +pub struct LocalCandidateStats { + #[serde(flatten)] + pub rtc: dictionaries::RtcStats, + + #[serde(flatten)] + pub local_candidate: dictionaries::IceCandidateStats, +} + +#[derive(Debug, Default, Clone, Deserialize)] +pub struct RemoteCandidateStats { + #[serde(flatten)] + pub rtc: dictionaries::RtcStats, + + #[serde(flatten)] + pub remote_candidate: dictionaries::IceCandidateStats, +} + +#[derive(Debug, Default, Clone, Deserialize)] +pub struct CertificateStats { + #[serde(flatten)] + pub rtc: dictionaries::RtcStats, + + #[serde(flatten)] + pub certificate: dictionaries::CertificateStats, +} + +#[derive(Debug, Default, Clone, Deserialize)] +pub struct TrackStats {} + +pub mod dictionaries { + use super::*; + + #[derive(Debug, Default, Clone, Deserialize)] + #[serde(rename_all = "camelCase")] + #[serde(default)] + pub struct RtcStats { + pub id: String, + pub timestamp: i64, + } + + #[derive(Debug, Default, Clone, Deserialize)] + #[serde(rename_all = "camelCase")] + #[serde(default)] + pub struct CodecStats { + pub payload_type: u32, + pub transport_id: String, + pub mime_type: String, + pub clock_rate: u32, + pub channels: u32, + pub sdp_fmtp_line: String, + } + + #[derive(Debug, Default, Clone, Deserialize)] + #[serde(rename_all = "camelCase")] + #[serde(default)] + pub struct RtpStreamStats { + pub ssrc: u32, + pub kind: String, + pub transport_id: String, + pub codec_id: String, + } + + #[derive(Debug, Default, Clone, Deserialize)] + #[serde(rename_all = "camelCase")] + #[serde(default)] + pub struct ReceivedRtpStreamStats { + pub packets_received: u64, + pub packets_lost: i64, + pub jitter: f64, + } + + #[derive(Debug, Default, Clone, Deserialize)] + #[serde(rename_all = "camelCase")] + #[serde(default)] + pub struct InboundRtpStreamStats { + pub track_identifier: String, + pub mid: String, + pub remote_id: String, + pub frames_decoded: u32, + pub key_frames_decoded: u32, + pub frames_rendered: u32, + pub frames_dropped: u32, + pub frame_width: u32, + pub frame_height: u32, + pub frames_per_second: f64, + pub qp_sum: u64, + pub total_decode_time: f64, + pub total_inter_frame_delay: f64, + pub total_squared_inter_frame_delay: f64, + pub pause_count: u32, + pub total_pause_duration: f64, + pub freeze_count: u32, + pub total_freeze_duration: f64, + pub last_packet_received_timestamp: f64, + pub header_bytes_received: u64, + pub packets_discarded: u64, + pub fec_bytes_received: u64, + pub fec_packets_received: u64, + pub fec_packets_discarded: u64, + pub bytes_received: u64, + pub nack_count: u32, + pub fir_count: u32, + pub pli_count: u32, + pub total_processing_delay: f64, + pub estimated_playout_timestamp: f64, + pub jitter_buffer_delay: f64, + pub jitter_buffer_target_delay: f64, + pub jitter_buffer_emitted_count: u64, + pub jitter_buffer_minimum_delay: f64, + pub total_samples_received: u64, + pub concealed_samples: u64, + pub silent_concealed_samples: u64, + pub concealment_events: u64, + pub inserted_samples_for_deceleration: u64, + pub removed_samples_for_acceleration: u64, + pub audio_level: f64, + pub total_audio_energy: f64, + pub total_samples_duration: f64, + pub frames_received: u64, + pub decoder_implementation: String, + pub playout_id: String, + pub power_efficient_decoder: bool, + pub frames_assembled_from_multiple_packets: u64, + pub total_assembly_time: f64, + pub retransmitted_packets_received: u64, + pub retransmitted_bytes_received: u64, + pub rtx_ssrc: u32, + pub fec_ssrc: u32, + } + + #[derive(Debug, Default, Clone, Deserialize)] + #[serde(rename_all = "camelCase")] + #[serde(default)] + pub struct SentRtpStreamStats { + pub packets_sent: u64, + pub bytes_sent: u64, + } + + #[derive(Debug, Default, Clone, Deserialize)] + #[serde(rename_all = "camelCase")] + #[serde(default)] + pub struct OutboundRtpStreamStats { + pub mid: String, + pub media_source_id: String, + pub remote_id: String, + pub rid: String, + pub header_bytes_sent: u64, + pub retransmitted_packets_sent: u64, + pub retransmitted_bytes_sent: u64, + pub rtx_ssrc: u32, + pub target_bitrate: f64, + pub total_encoded_bytes_target: u64, + pub frame_width: u32, + pub frame_height: u32, + pub frames_per_second: f64, + pub frames_sent: u32, + pub huge_frames_sent: u32, + pub frames_encoded: u32, + pub key_frames_encoded: u32, + pub qp_sum: u64, + pub total_encode_time: f64, + pub total_packet_send_delay: f64, + pub quality_limitation_reason: QualityLimitationReason, + pub quality_limitation_durations: HashMap, + pub quality_limitation_resolution_changes: u32, + pub nack_count: u32, + pub fir_count: u32, + pub pli_count: u32, + pub encoder_implementation: String, + pub power_efficient_encoder: bool, + pub active: bool, + pub scalibility_mode: String, + } + + #[derive(Debug, Default, Clone, Deserialize)] + #[serde(rename_all = "camelCase")] + #[serde(default)] + pub struct RemoteInboundRtpStreamStats { + pub local_id: String, + pub round_trip_time: f64, + pub total_round_trip_time: f64, + pub fraction_lost: f64, + pub round_trip_time_measurements: u64, + } + + #[derive(Debug, Default, Clone, Deserialize)] + #[serde(rename_all = "camelCase")] + #[serde(default)] + pub struct RemoteOutboundRtpStreamStats { + pub local_id: String, + pub remote_timestamp: f64, + pub reports_sent: u64, + pub round_trip_time: f64, + pub total_round_trip_time: f64, + pub round_trip_time_measurements: u64, + } + + #[derive(Debug, Default, Clone, Deserialize)] + #[serde(rename_all = "camelCase")] + #[serde(default)] + pub struct MediaSourceStats { + pub track_identifier: String, + pub kind: String, + } + + #[derive(Debug, Default, Clone, Deserialize)] + #[serde(rename_all = "camelCase")] + #[serde(default)] + pub struct AudioSourceStats { + pub audio_level: f64, + pub total_audio_energy: f64, + pub total_samples_duration: f64, + pub echo_return_loss: f64, + pub echo_return_loss_enhancement: f64, + pub dropped_samples_duration: f64, + pub dropped_samples_events: u32, + pub total_capture_delay: f64, + pub total_samples_captured: u64, + } + + #[derive(Debug, Default, Clone, Deserialize)] + #[serde(rename_all = "camelCase")] + #[serde(default)] + pub struct VideoSourceStats { + pub width: u32, + pub height: u32, + pub frames: u32, + pub frames_per_second: f64, + } + + #[derive(Debug, Default, Clone, Deserialize)] + #[serde(rename_all = "camelCase")] + #[serde(default)] + pub struct AudioPlayoutStats { + pub kind: String, + pub synthesized_samples_duration: f64, + pub synthesized_samples_events: u32, + pub total_samples_duration: f64, + pub total_playout_delay: f64, + pub total_samples_count: u64, + } + + #[derive(Debug, Default, Clone, Deserialize)] + #[serde(rename_all = "camelCase")] + #[serde(default)] + pub struct PeerConnectionStats { + pub data_channels_opened: u32, + pub data_channels_closed: u32, + } + + #[derive(Debug, Default, Clone, Deserialize)] + #[serde(rename_all = "camelCase")] + #[serde(default)] + pub struct DataChannelStats { + pub label: String, + pub protocol: String, + pub data_channel_identifier: i32, + pub state: Option, + pub messages_sent: u32, + pub bytes_sent: u64, + pub messages_received: u32, + pub bytes_received: u64, + } + + #[derive(Debug, Default, Clone, Deserialize)] + #[serde(rename_all = "camelCase")] + #[serde(default)] + pub struct TransportStats { + pub packets_sent: u64, + pub packets_received: u64, + pub bytes_sent: u64, + pub bytes_received: u64, + pub ice_role: IceRole, + pub ice_local_username_fragment: String, + pub dtls_state: Option, + pub ice_state: Option, + pub selected_candidate_pair_id: String, + pub local_certificate_id: String, + pub remote_certificate_id: String, + pub tls_version: String, + pub dtls_cipher: String, + pub dtls_role: DtlsRole, + pub srtp_cipher: String, + pub selected_candidate_pair_changes: u32, + } + + #[derive(Debug, Default, Clone, Deserialize)] + #[serde(rename_all = "camelCase")] + #[serde(default)] + pub struct CandidatePairStats { + pub transport_id: String, + pub local_candidate_id: String, + pub remote_candidate_id: String, + pub state: Option, + pub nominated: bool, + pub packets_sent: u64, + pub packets_received: u64, + pub bytes_sent: u64, + pub bytes_received: u64, + pub last_packet_sent_timestamp: f64, + pub last_packet_received_timestamp: f64, + pub total_round_trip_time: f64, + pub current_round_trip_time: f64, + pub available_outgoing_bitrate: f64, + pub available_incoming_bitrate: f64, + pub requests_received: u64, + pub requests_sent: u64, + pub responses_received: u64, + pub responses_sent: u64, + pub consent_requests_sent: u64, + pub packets_discarded_on_send: u32, + pub bytes_discarded_on_send: u64, + } + + #[derive(Debug, Default, Clone, Deserialize)] + #[serde(rename_all = "camelCase")] + #[serde(default)] + pub struct IceCandidateStats { + pub transport_id: String, + pub address: String, + pub port: i32, + pub protocol: String, + pub candidate_type: Option, + pub priority: i32, + pub url: String, + pub relay_protocol: Option, + pub foundation: String, + pub related_address: String, + pub related_port: i32, + pub username_fragment: String, + pub tcp_type: Option, + } + + #[derive(Debug, Default, Clone, Deserialize)] + #[serde(rename_all = "camelCase")] + #[serde(default)] + pub struct CertificateStats { + pub fingerprint: String, + pub fingerprint_algorithm: String, + pub base64_certificate: String, + pub issuer_certificate_id: String, + } +} diff --git a/livekit-ffi/generate_proto.sh b/livekit-ffi/generate_proto.sh index 6150dba8..19a1ccc1 100755 --- a/livekit-ffi/generate_proto.sh +++ b/livekit-ffi/generate_proto.sh @@ -27,4 +27,5 @@ protoc \ $PROTOCOL/participant.proto \ $PROTOCOL/video_frame.proto \ $PROTOCOL/audio_frame.proto \ - $PROTOCOL/e2ee.proto + $PROTOCOL/e2ee.proto \ + $PROTOCOL/stats.proto diff --git a/livekit-ffi/protocol/stats.proto b/livekit-ffi/protocol/stats.proto new file mode 100644 index 00000000..45df5c9b --- /dev/null +++ b/livekit-ffi/protocol/stats.proto @@ -0,0 +1,449 @@ +// Copyright 2023 LiveKit, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package livekit.proto; +option csharp_namespace = "LiveKit.Proto"; + + +enum DataChannelState { + DC_CONNECTING = 0; + DC_OPEN = 1; + DC_CLOSING = 2; + DC_CLOSED = 3; +} + +enum QualityLimitationReason { + LIMITATION_NONE = 0; + LIMITATION_CPU = 1; + LIMITATION_BANDWIDTH = 2; + LIMITATION_OTHER = 3; +} + +enum IceRole { + ICE_UNKNOWN = 0; + ICE_CONTROLLING = 1; + ICE_CONTROLLED = 2; +} + +enum DtlsTransportState { + DTLS_TRANSPORT_NEW = 0; + DTLS_TRANSPORT_CONNECTING = 1; + DTLS_TRANSPORT_CONNECTED = 2; + DTLS_TRANSPORT_CLOSED = 3; + DTLS_TRANSPORT_FAILED = 4; +} + +enum IceTransportState { + ICE_TRANSPORT_NEW = 0; + ICE_TRANSPORT_CHECKING = 1; + ICE_TRANSPORT_CONNECTED = 2; + ICE_TRANSPORT_COMPLETED = 3; + ICE_TRANSPORT_DISCONNECTED = 4; + ICE_TRANSPORT_FAILED = 5; + ICE_TRANSPORT_CLOSED = 6; +} + +enum DtlsRole { + DTLS_CLIENT = 0; + DTLS_SERVER = 1; + DTLS_UNKNOWN = 2; +} + +enum IceCandidatePairState { + PAIR_FROZEN = 0; + PAIR_WAITING = 1; + PAIR_IN_PROGRESS = 2; + PAIR_FAILED = 3; + PAIR_SUCCEEDED = 4; +} + +enum IceCandidateType { + HOST = 0; + SRFLX = 1; + PRFLX = 2; + RELAY = 3; +} + +enum IceServerTransportProtocol { + TRANSPORT_UDP = 0; + TRANSPORT_TCP = 1; + TRANSPORT_TLS = 2; +} + +enum IceTcpCandidateType { + CANDIDATE_ACTIVE = 0; + CANDIDATE_PASSIVE = 1; + CANDIDATE_SO = 2; +} + +message RtcStats { + message Codec { + RtcStatsData rtc = 1; + CodecStats codec = 2; + } + + message InboundRtp { + RtcStatsData rtc = 1; + RtpStreamStats stream = 2; + ReceivedRtpStreamStats received = 3; + InboundRtpStreamStats inbound = 4; + } + + message OutboundRtp { + RtcStatsData rtc = 1; + RtpStreamStats stream = 2; + SentRtpStreamStats sent = 3; + OutboundRtpStreamStats outbound = 4; + } + + message RemoteInboundRtp { + RtcStatsData rtc = 1; + RtpStreamStats stream = 2; + ReceivedRtpStreamStats received = 3; + RemoteInboundRtpStreamStats remote_inbound = 4; + } + + message RemoteOutboundRtp { + RtcStatsData rtc = 1; + RtpStreamStats stream = 2; + SentRtpStreamStats sent = 3; + RemoteOutboundRtpStreamStats remote_outbound = 4; + } + + message MediaSource { + RtcStatsData rtc = 1; + MediaSourceStats source = 2; + AudioSourceStats audio = 3; + VideoSourceStats video = 4; + } + + message MediaPlayout { + RtcStatsData rtc = 1; + AudioPlayoutStats audio_playout = 2; + } + + message PeerConnection { + RtcStatsData rtc = 1; + PeerConnectionStats pc = 2; + } + + message DataChannel { + RtcStatsData rtc = 1; + DataChannelStats dc = 2; + } + + message Transport { + RtcStatsData rtc = 1; + TransportStats transport = 2; + } + + message CandidatePair { + RtcStatsData rtc = 1; + CandidatePairStats candidate_pair = 2; + } + + message LocalCandidate { + RtcStatsData rtc = 1; + IceCandidateStats candidate = 2; + } + + message RemoteCandidate { + RtcStatsData rtc = 1; + IceCandidateStats candidate = 2; + } + + message Certificate { + RtcStatsData rtc = 1; + CertificateStats certificate = 2; + } + + message Track { + // Deprecated + } + + oneof stats { + Codec codec = 3; + InboundRtp inbound_rtp = 4; + OutboundRtp outbound_rtp = 5; + RemoteInboundRtp remote_inbound_rtp = 6; + RemoteOutboundRtp remote_outbound_rtp = 7; + MediaSource media_source = 8; + MediaPlayout media_playout = 9; + PeerConnection peer_connection = 10; + DataChannel data_channel = 11; + Transport transport = 12; + CandidatePair candidate_pair = 13; + LocalCandidate local_candidate = 14; + RemoteCandidate remote_candidate = 15; + Certificate certificate = 16; + Track track = 17; + } +} + +message RtcStatsData { + string id = 1; + int64 timestamp = 2; +} + +message CodecStats { + uint32 payload_type = 1; + string transport_id = 2; + string mime_type = 3; + uint32 clock_rate = 4; + uint32 channels = 5; + string sdp_fmtp_line = 6; +} + +message RtpStreamStats { + uint32 ssrc = 1; + string kind = 2; + string transport_id = 3; + string codec_id = 4; +} + +message ReceivedRtpStreamStats { + uint64 packets_received = 1; + int64 packets_lost = 2; + double jitter = 3; +} + +message InboundRtpStreamStats { + string track_identifier = 1; + string mid = 2; + string remote_id = 3; + uint32 frames_decoded = 4; + uint32 key_frames_decoded = 5; + uint32 frames_rendered = 6; + uint32 frames_dropped = 7; + uint32 frame_width = 8; + uint32 frame_height = 9; + double frames_per_second = 10; + uint64 qp_sum = 11; + double total_decode_time = 12; + double total_inter_frame_delay = 13; + double total_squared_inter_frame_delay = 14; + uint32 pause_count = 15; + double total_pause_duration = 16; + uint32 freeze_count = 17; + double total_freeze_duration = 18; + double last_packet_received_timestamp = 19; + uint64 header_bytes_received = 20; + uint64 packets_discarded = 21; + uint64 fec_bytes_received = 22; + uint64 fec_packets_received = 23; + uint64 fec_packets_discarded = 24; + uint64 bytes_received = 25; + uint32 nack_count = 26; + uint32 fir_count = 27; + uint32 pli_count = 28; + double total_processing_delay = 29; + double estimated_playout_timestamp = 30; + double jitter_buffer_delay = 31; + double jitter_buffer_target_delay = 32; + uint64 jitter_buffer_emitted_count = 33; + double jitter_buffer_minimum_delay = 34; + uint64 total_samples_received = 35; + uint64 concealed_samples = 36; + uint64 silent_concealed_samples = 37; + uint64 concealment_events = 38; + uint64 inserted_samples_for_deceleration = 39; + uint64 removed_samples_for_acceleration = 40; + double audio_level = 41; + double total_audio_energy = 42; + double total_samples_duration = 43; + uint64 frames_received = 44; + string decoder_implementation = 45; + string playout_id = 46; + bool power_efficient_decoder = 47; + uint64 frames_assembled_from_multiple_packets = 48; + double total_assembly_time = 49; + uint64 retransmitted_packets_received = 50; + uint64 retransmitted_bytes_received = 51; + uint32 rtx_ssrc = 52; + uint32 fec_ssrc = 53; +} + +message SentRtpStreamStats { + uint64 packets_sent = 1; + uint64 bytes_sent = 2; +} + +message OutboundRtpStreamStats { + string mid = 1; + string media_source_id = 2; + string remote_id = 3; + string rid = 4; + uint64 header_bytes_sent = 5; + uint64 retransmitted_packets_sent = 6; + uint64 retransmitted_bytes_sent = 7; + uint32 rtx_ssrc = 8; + double target_bitrate = 9; + uint64 total_encoded_bytes_target = 10; + uint32 frame_width = 11; + uint32 frame_height = 12; + double frames_per_second = 13; + uint32 frames_sent = 14; + uint32 huge_frames_sent = 15; + uint32 frames_encoded = 16; + uint32 key_frames_encoded = 17; + uint64 qp_sum = 18; + double total_encode_time = 19; + double total_packet_send_delay = 20; + QualityLimitationReason quality_limitation_reason = 21; + map quality_limitation_durations = 22; + uint32 quality_limitation_resolution_changes = 23; + uint32 nack_count = 24; + uint32 fir_count = 25; + uint32 pli_count = 26; + string encoder_implementation = 27; + bool power_efficient_encoder = 28; + bool active = 29; + string scalibility_mode = 30; +} + +message RemoteInboundRtpStreamStats { + string local_id = 1; + double round_trip_time = 2; + double total_round_trip_time = 3; + double fraction_lost = 4; + uint64 round_trip_time_measurements = 5; +} + +message RemoteOutboundRtpStreamStats { + string local_id = 1; + double remote_timestamp = 2; + uint64 reports_sent = 3; + double round_trip_time = 4; + double total_round_trip_time = 5; + uint64 round_trip_time_measurements = 6; +} + +message MediaSourceStats { + string track_identifier = 1; + string kind = 2; +} + +message AudioSourceStats { + double audio_level = 1; + double total_audio_energy = 2; + double total_samples_duration = 3; + double echo_return_loss = 4; + double echo_return_loss_enhancement = 5; + double dropped_samples_duration = 6; + uint32 dropped_samples_events = 7; + double total_capture_delay = 8; + uint64 total_samples_captured = 9; +} + +message VideoSourceStats { + uint32 width = 1; + uint32 height = 2; + uint32 frames = 3; + double frames_per_second = 4; +} + +message AudioPlayoutStats { + string kind = 1; + double synthesized_samples_duration = 2; + uint32 synthesized_samples_events = 3; + double total_samples_duration = 4; + double total_playout_delay = 5; + uint64 total_samples_count = 6; +} + +message PeerConnectionStats { + uint32 data_channels_opened = 1; + uint32 data_channels_closed = 2; +} + +message DataChannelStats { + string label = 1; + string protocol = 2; + int32 data_channel_identifier = 3; + optional DataChannelState state = 4; + uint32 messages_sent = 5; + uint64 bytes_sent = 6; + uint32 messages_received = 7; + uint64 bytes_received = 8; +} + +message TransportStats { + uint64 packets_sent = 1; + uint64 packets_received = 2; + uint64 bytes_sent = 3; + uint64 bytes_received = 4; + IceRole ice_role = 5; + string ice_local_username_fragment = 6; + optional DtlsTransportState dtls_state = 7; + optional IceTransportState ice_state = 8; + string selected_candidate_pair_id = 9; + string local_certificate_id = 10; + string remote_certificate_id = 11; + string tls_version = 12; + string dtls_cipher = 13; + DtlsRole dtls_role = 14; + string srtp_cipher = 15; + uint32 selected_candidate_pair_changes = 16; +} + +message CandidatePairStats { + string transport_id = 1; + string local_candidate_id = 2; + string remote_candidate_id = 3; + optional IceCandidatePairState state = 4; + bool nominated = 5; + uint64 packets_sent = 6; + uint64 packets_received = 7; + uint64 bytes_sent = 8; + uint64 bytes_received = 9; + double last_packet_sent_timestamp = 10; + double last_packet_received_timestamp = 11; + double total_round_trip_time = 12; + double current_round_trip_time = 13; + double available_outgoing_bitrate = 14; + double available_incoming_bitrate = 15; + uint64 requests_received = 16; + uint64 requests_sent = 17; + uint64 responses_received = 18; + uint64 responses_sent = 19; + uint64 consent_requests_sent = 20; + uint32 packets_discarded_on_send = 21; + uint64 bytes_discarded_on_send = 22; +} + +message IceCandidateStats { + string transport_id = 1; + string address = 2; + int32 port = 3; + string protocol = 4; + optional IceCandidateType candidate_type = 5; + int32 priority = 6; + string url = 7; + optional IceServerTransportProtocol relay_protocol = 8; + string foundation = 9; + string related_address = 10; + int32 related_port = 11; + string username_fragment = 12; + optional IceTcpCandidateType tcp_type = 13; +} + +message CertificateStats { + string fingerprint = 1; + string fingerprint_algorithm = 2; + string base64_certificate = 3; + string issuer_certificate_id = 4; +} + diff --git a/livekit-ffi/src/conversion/mod.rs b/livekit-ffi/src/conversion/mod.rs index 82f3ee7a..76025aac 100644 --- a/livekit-ffi/src/conversion/mod.rs +++ b/livekit-ffi/src/conversion/mod.rs @@ -15,5 +15,6 @@ pub mod audio_frame; pub mod participant; pub mod room; +pub mod stats; pub mod track; pub mod video_frame; diff --git a/livekit-ffi/src/conversion/stats.rs b/livekit-ffi/src/conversion/stats.rs new file mode 100644 index 00000000..c0586b40 --- /dev/null +++ b/livekit-ffi/src/conversion/stats.rs @@ -0,0 +1,584 @@ +use crate::proto; +use livekit::webrtc::{ + prelude::DataChannelState, + stats::{ + self as rtc, DtlsRole, DtlsTransportState, IceCandidatePairState, IceCandidateType, + IceRole, IceServerTransportProtocol, IceTcpCandidateType, IceTransportState, + QualityLimitationReason, + }, +}; + +impl From for proto::DataChannelState { + fn from(value: DataChannelState) -> Self { + match value { + DataChannelState::Connecting => Self::DcConnecting, + DataChannelState::Open => Self::DcOpen, + DataChannelState::Closing => Self::DcClosing, + DataChannelState::Closed => Self::DcClosed, + } + } +} + +impl From for proto::QualityLimitationReason { + fn from(value: QualityLimitationReason) -> Self { + match value { + QualityLimitationReason::None => Self::LimitationNone, + QualityLimitationReason::Cpu => Self::LimitationCpu, + QualityLimitationReason::Bandwidth => Self::LimitationBandwidth, + QualityLimitationReason::Other => Self::LimitationOther, + } + } +} + +impl From for proto::IceRole { + fn from(value: IceRole) -> Self { + match value { + IceRole::Unknown => Self::IceUnknown, + IceRole::Controlling => Self::IceControlling, + IceRole::Controlled => Self::IceControlled, + } + } +} + +impl From for proto::DtlsTransportState { + fn from(value: DtlsTransportState) -> Self { + match value { + DtlsTransportState::New => Self::DtlsTransportNew, + DtlsTransportState::Connecting => Self::DtlsTransportConnecting, + DtlsTransportState::Connected => Self::DtlsTransportConnected, + DtlsTransportState::Closed => Self::DtlsTransportClosed, + DtlsTransportState::Failed => Self::DtlsTransportFailed, + } + } +} + +impl From for proto::IceTransportState { + fn from(value: IceTransportState) -> Self { + match value { + IceTransportState::New => Self::IceTransportNew, + IceTransportState::Checking => Self::IceTransportChecking, + IceTransportState::Connected => Self::IceTransportConnected, + IceTransportState::Completed => Self::IceTransportCompleted, + IceTransportState::Disconnected => Self::IceTransportDisconnected, + IceTransportState::Failed => Self::IceTransportFailed, + IceTransportState::Closed => Self::IceTransportClosed, + } + } +} + +impl From for proto::DtlsRole { + fn from(value: DtlsRole) -> Self { + match value { + DtlsRole::Unknown => Self::DtlsUnknown, + DtlsRole::Client => Self::DtlsClient, + DtlsRole::Server => Self::DtlsServer, + } + } +} + +impl From for proto::IceCandidatePairState { + fn from(value: IceCandidatePairState) -> Self { + match value { + IceCandidatePairState::Frozen => Self::PairFrozen, + IceCandidatePairState::Waiting => Self::PairWaiting, + IceCandidatePairState::InProgress => Self::PairInProgress, + IceCandidatePairState::Failed => Self::PairFailed, + IceCandidatePairState::Succeeded => Self::PairSucceeded, + } + } +} + +impl From for proto::IceServerTransportProtocol { + fn from(value: IceServerTransportProtocol) -> Self { + match value { + IceServerTransportProtocol::Udp => Self::TransportUdp, + IceServerTransportProtocol::Tcp => Self::TransportTcp, + IceServerTransportProtocol::Tls => Self::TransportTls, + } + } +} + +impl From for proto::IceCandidateType { + fn from(value: IceCandidateType) -> Self { + match value { + IceCandidateType::Host => Self::Host, + IceCandidateType::Srflx => Self::Srflx, + IceCandidateType::Prflx => Self::Prflx, + IceCandidateType::Relay => Self::Relay, + } + } +} + +impl From for proto::IceTcpCandidateType { + fn from(value: IceTcpCandidateType) -> Self { + match value { + IceTcpCandidateType::Active => Self::CandidateActive, + IceTcpCandidateType::Passive => Self::CandidatePassive, + IceTcpCandidateType::So => Self::CandidateSo, + } + } +} + +impl From for proto::RtcStats { + fn from(value: rtc::RtcStats) -> Self { + let stats = match value { + rtc::RtcStats::Codec { rtc, codec } => { + proto::rtc_stats::Stats::Codec(proto::rtc_stats::Codec { + rtc: Some(rtc.into()), + codec: Some(codec.into()), + }) + } + rtc::RtcStats::InboundRtp { + rtc, + stream, + received, + inbound, + } => proto::rtc_stats::Stats::InboundRtp(proto::rtc_stats::InboundRtp { + rtc: Some(rtc.into()), + stream: Some(stream.into()), + received: Some(received.into()), + inbound: Some(inbound.into()), + }), + rtc::RtcStats::OutboundRtp { + rtc, + stream, + sent, + outbound, + } => proto::rtc_stats::Stats::OutboundRtp(proto::rtc_stats::OutboundRtp { + rtc: Some(rtc.into()), + stream: Some(stream.into()), + sent: Some(sent.into()), + outbound: Some(outbound.into()), + }), + rtc::RtcStats::RemoteInboundRtp { + rtc, + stream, + received, + remote_inbound, + } => proto::rtc_stats::Stats::RemoteInboundRtp(proto::rtc_stats::RemoteInboundRtp { + rtc: Some(rtc.into()), + stream: Some(stream.into()), + received: Some(received.into()), + remote_inbound: Some(remote_inbound.into()), + }), + rtc::RtcStats::RemoteOutboundRtp { + rtc, + stream, + sent, + remote_outbound, + } => proto::rtc_stats::Stats::RemoteOutboundRtp(proto::rtc_stats::RemoteOutboundRtp { + rtc: Some(rtc.into()), + stream: Some(stream.into()), + sent: Some(sent.into()), + remote_outbound: Some(remote_outbound.into()), + }), + rtc::RtcStats::MediaSource { + rtc, + source, + audio, + video, + } => proto::rtc_stats::Stats::MediaSource(proto::rtc_stats::MediaSource { + rtc: Some(rtc.into()), + source: Some(source.into()), + audio: Some(audio.into()), + video: Some(video.into()), + }), + rtc::RtcStats::MediaPlayout { rtc, audio_playout } => { + proto::rtc_stats::Stats::MediaPlayout(proto::rtc_stats::MediaPlayout { + rtc: Some(rtc.into()), + audio_playout: Some(audio_playout.into()), + }) + } + rtc::RtcStats::PeerConnection { rtc, pc } => { + proto::rtc_stats::Stats::PeerConnection(proto::rtc_stats::PeerConnection { + rtc: Some(rtc.into()), + pc: Some(pc.into()), + }) + } + rtc::RtcStats::DataChannel { rtc, dc } => { + proto::rtc_stats::Stats::DataChannel(proto::rtc_stats::DataChannel { + rtc: Some(rtc.into()), + dc: Some(dc.into()), + }) + } + rtc::RtcStats::Transport { rtc, transport } => { + proto::rtc_stats::Stats::Transport(proto::rtc_stats::Transport { + rtc: Some(rtc.into()), + transport: Some(transport.into()), + }) + } + rtc::RtcStats::CandidatePair { + rtc, + candidate_pair, + } => proto::rtc_stats::Stats::CandidatePair(proto::rtc_stats::CandidatePair { + rtc: Some(rtc.into()), + candidate_pair: Some(candidate_pair.into()), + }), + rtc::RtcStats::LocalCandidate { rtc, candidate } => { + proto::rtc_stats::Stats::LocalCandidate(proto::rtc_stats::LocalCandidate { + rtc: Some(rtc.into()), + candidate: Some(candidate.into()), + }) + } + rtc::RtcStats::RemoteCandidate { rtc, candidate } => { + proto::rtc_stats::Stats::RemoteCandidate(proto::rtc_stats::RemoteCandidate { + rtc: Some(rtc.into()), + candidate: Some(candidate.into()), + }) + } + rtc::RtcStats::Certificate { rtc, certificate } => { + proto::rtc_stats::Stats::Certificate(proto::rtc_stats::Certificate { + rtc: Some(rtc.into()), + certificate: Some(certificate.into()), + }) + } + rtc::RtcStats::Track {} => proto::rtc_stats::Stats::Track(proto::rtc_stats::Track {}), + }; + + proto::RtcStats { stats: Some(stats) } + } +} + +impl From for proto::RtcStatsData { + fn from(value: rtc::RtcStatsData) -> Self { + Self { + id: value.id, + timestamp: value.timestamp, + } + } +} + +impl From for proto::CodecStats { + fn from(value: rtc::CodecStats) -> Self { + Self { + payload_type: value.payload_type, + transport_id: value.transport_id, + mime_type: value.mime_type, + clock_rate: value.clock_rate, + channels: value.channels, + sdp_fmtp_line: value.sdp_fmtp_line, + } + } +} + +impl From for proto::RtpStreamStats { + fn from(value: rtc::RtpStreamStats) -> Self { + Self { + ssrc: value.ssrc, + kind: value.kind, + transport_id: value.transport_id, + codec_id: value.codec_id, + } + } +} + +impl From for proto::ReceivedRtpStreamStats { + fn from(value: rtc::ReceivedRtpStreamStats) -> Self { + Self { + packets_received: value.packets_received, + packets_lost: value.packets_lost, + jitter: value.jitter, + } + } +} + +impl From for proto::InboundRtpStreamStats { + fn from(value: rtc::InboundRtpStreamStats) -> Self { + Self { + track_identifier: value.track_identifier, + mid: value.mid, + remote_id: value.remote_id, + frames_decoded: value.frames_decoded, + key_frames_decoded: value.key_frames_decoded, + frames_rendered: value.frames_rendered, + frames_dropped: value.frames_dropped, + frame_width: value.frame_width, + frame_height: value.frame_height, + frames_per_second: value.frames_per_second, + qp_sum: value.qp_sum, + total_decode_time: value.total_decode_time, + total_inter_frame_delay: value.total_inter_frame_delay, + total_squared_inter_frame_delay: value.total_squared_inter_frame_delay, + pause_count: value.pause_count, + total_pause_duration: value.total_pause_duration, + freeze_count: value.freeze_count, + total_freeze_duration: value.total_freeze_duration, + last_packet_received_timestamp: value.last_packet_received_timestamp, + header_bytes_received: value.header_bytes_received, + packets_discarded: value.packets_discarded, + fec_bytes_received: value.fec_bytes_received, + fec_packets_received: value.fec_packets_received, + fec_packets_discarded: value.fec_packets_discarded, + bytes_received: value.bytes_received, + nack_count: value.nack_count, + fir_count: value.fir_count, + pli_count: value.pli_count, + total_processing_delay: value.total_processing_delay, + estimated_playout_timestamp: value.estimated_playout_timestamp, + jitter_buffer_delay: value.jitter_buffer_delay, + jitter_buffer_target_delay: value.jitter_buffer_target_delay, + jitter_buffer_emitted_count: value.jitter_buffer_emitted_count, + jitter_buffer_minimum_delay: value.jitter_buffer_minimum_delay, + total_samples_received: value.total_samples_received, + concealed_samples: value.concealed_samples, + silent_concealed_samples: value.silent_concealed_samples, + concealment_events: value.concealment_events, + inserted_samples_for_deceleration: value.inserted_samples_for_deceleration, + removed_samples_for_acceleration: value.removed_samples_for_acceleration, + audio_level: value.audio_level, + total_audio_energy: value.total_audio_energy, + total_samples_duration: value.total_samples_duration, + frames_received: value.frames_received, + decoder_implementation: value.decoder_implementation, + playout_id: value.playout_id, + power_efficient_decoder: value.power_efficient_decoder, + frames_assembled_from_multiple_packets: value.frames_assembled_from_multiple_packets, + total_assembly_time: value.total_assembly_time, + retransmitted_packets_received: value.retransmitted_packets_received, + retransmitted_bytes_received: value.retransmitted_bytes_received, + rtx_ssrc: value.rtx_ssrc, + fec_ssrc: value.fec_ssrc, + } + } +} + +impl From for proto::SentRtpStreamStats { + fn from(value: rtc::SentRtpStreamStats) -> Self { + Self { + packets_sent: value.packets_sent, + bytes_sent: value.bytes_sent, + } + } +} + +impl From for proto::OutboundRtpStreamStats { + fn from(value: rtc::OutboundRtpStreamStats) -> Self { + Self { + mid: value.mid, + media_source_id: value.media_source_id, + remote_id: value.remote_id, + rid: value.rid, + header_bytes_sent: value.header_bytes_sent, + retransmitted_packets_sent: value.retransmitted_packets_sent, + retransmitted_bytes_sent: value.retransmitted_bytes_sent, + rtx_ssrc: value.rtx_ssrc, + target_bitrate: value.target_bitrate, + total_encoded_bytes_target: value.total_encoded_bytes_target, + frame_width: value.frame_width, + frame_height: value.frame_height, + frames_per_second: value.frames_per_second, + frames_sent: value.frames_sent, + huge_frames_sent: value.huge_frames_sent, + frames_encoded: value.frames_encoded, + key_frames_encoded: value.key_frames_encoded, + qp_sum: value.qp_sum, + total_encode_time: value.total_encode_time, + total_packet_send_delay: value.total_packet_send_delay, + quality_limitation_reason: proto::QualityLimitationReason::from( + value.quality_limitation_reason, + ) as i32, + quality_limitation_durations: value.quality_limitation_durations, + quality_limitation_resolution_changes: value.quality_limitation_resolution_changes, + nack_count: value.nack_count, + fir_count: value.fir_count, + pli_count: value.pli_count, + encoder_implementation: value.encoder_implementation, + power_efficient_encoder: value.power_efficient_encoder, + active: value.active, + scalibility_mode: value.scalibility_mode, + } + } +} + +impl From for proto::RemoteInboundRtpStreamStats { + fn from(value: rtc::RemoteInboundRtpStreamStats) -> Self { + Self { + local_id: value.local_id, + round_trip_time: value.round_trip_time, + total_round_trip_time: value.total_round_trip_time, + fraction_lost: value.fraction_lost, + round_trip_time_measurements: value.round_trip_time_measurements, + } + } +} + +impl From for proto::RemoteOutboundRtpStreamStats { + fn from(value: rtc::RemoteOutboundRtpStreamStats) -> Self { + Self { + local_id: value.local_id, + remote_timestamp: value.remote_timestamp, + reports_sent: value.reports_sent, + round_trip_time: value.round_trip_time, + total_round_trip_time: value.total_round_trip_time, + round_trip_time_measurements: value.round_trip_time_measurements, + } + } +} + +impl From for proto::MediaSourceStats { + fn from(value: rtc::MediaSourceStats) -> Self { + Self { + track_identifier: value.track_identifier, + kind: value.kind, + } + } +} + +impl From for proto::AudioSourceStats { + fn from(value: rtc::AudioSourceStats) -> Self { + Self { + audio_level: value.audio_level, + total_audio_energy: value.total_audio_energy, + total_samples_duration: value.total_samples_duration, + echo_return_loss: value.echo_return_loss, + echo_return_loss_enhancement: value.echo_return_loss_enhancement, + dropped_samples_duration: value.dropped_samples_duration, + dropped_samples_events: value.dropped_samples_events, + total_capture_delay: value.total_capture_delay, + total_samples_captured: value.total_samples_captured, + } + } +} + +impl From for proto::VideoSourceStats { + fn from(value: rtc::VideoSourceStats) -> Self { + Self { + width: value.width, + height: value.height, + frames: value.frames, + frames_per_second: value.frames_per_second, + } + } +} + +impl From for proto::AudioPlayoutStats { + fn from(value: rtc::AudioPlayoutStats) -> Self { + Self { + kind: value.kind, + synthesized_samples_duration: value.synthesized_samples_duration, + synthesized_samples_events: value.synthesized_samples_events, + total_samples_duration: value.total_samples_duration, + total_playout_delay: value.total_playout_delay, + total_samples_count: value.total_samples_count, + } + } +} + +impl From for proto::PeerConnectionStats { + fn from(value: rtc::PeerConnectionStats) -> Self { + Self { + data_channels_opened: value.data_channels_opened, + data_channels_closed: value.data_channels_closed, + } + } +} + +impl From for proto::DataChannelStats { + fn from(value: rtc::DataChannelStats) -> Self { + Self { + label: value.label, + protocol: value.protocol, + data_channel_identifier: value.data_channel_identifier, + state: value.state.map(|v| proto::DataChannelState::from(v) as i32), + messages_sent: value.messages_sent, + bytes_sent: value.bytes_sent, + messages_received: value.messages_received, + bytes_received: value.bytes_received, + } + } +} + +impl From for proto::TransportStats { + fn from(value: rtc::TransportStats) -> Self { + Self { + packets_sent: value.packets_sent, + packets_received: value.packets_received, + bytes_sent: value.bytes_sent, + bytes_received: value.bytes_received, + ice_role: proto::IceRole::from(value.ice_role) as i32, + ice_local_username_fragment: value.ice_local_username_fragment, + dtls_state: value + .dtls_state + .map(|v| proto::DtlsTransportState::from(v) as i32), + ice_state: value + .ice_state + .map(|v| proto::IceTransportState::from(v) as i32), + selected_candidate_pair_id: value.selected_candidate_pair_id, + local_certificate_id: value.local_certificate_id, + remote_certificate_id: value.remote_certificate_id, + tls_version: value.tls_version, + dtls_cipher: value.dtls_cipher, + dtls_role: proto::DtlsRole::from(value.dtls_role) as i32, + srtp_cipher: value.srtp_cipher, + selected_candidate_pair_changes: value.selected_candidate_pair_changes, + } + } +} + +impl From for proto::CandidatePairStats { + fn from(value: rtc::CandidatePairStats) -> Self { + Self { + transport_id: value.transport_id, + local_candidate_id: value.local_candidate_id, + remote_candidate_id: value.remote_candidate_id, + state: value + .state + .map(|v| proto::IceCandidatePairState::from(v) as i32), + nominated: value.nominated, + packets_sent: value.packets_sent, + packets_received: value.packets_received, + bytes_sent: value.bytes_sent, + bytes_received: value.bytes_received, + last_packet_sent_timestamp: value.last_packet_sent_timestamp, + last_packet_received_timestamp: value.last_packet_received_timestamp, + total_round_trip_time: value.total_round_trip_time, + current_round_trip_time: value.current_round_trip_time, + available_outgoing_bitrate: value.available_outgoing_bitrate, + available_incoming_bitrate: value.available_incoming_bitrate, + requests_received: value.requests_received, + requests_sent: value.requests_sent, + responses_received: value.responses_received, + responses_sent: value.responses_sent, + consent_requests_sent: value.consent_requests_sent, + packets_discarded_on_send: value.packets_discarded_on_send, + bytes_discarded_on_send: value.bytes_discarded_on_send, + } + } +} + +impl From for proto::IceCandidateStats { + fn from(value: rtc::IceCandidateStats) -> Self { + Self { + transport_id: value.transport_id, + address: value.address, + port: value.port, + protocol: value.protocol, + candidate_type: value + .candidate_type + .map(|v| proto::IceCandidateType::from(v) as i32), + priority: value.priority, + url: value.url, + relay_protocol: value + .relay_protocol + .map(|v| proto::IceServerTransportProtocol::from(v) as i32), + foundation: value.foundation, + related_address: value.related_address, + related_port: value.related_port, + username_fragment: value.username_fragment, + tcp_type: value + .tcp_type + .map(|v| proto::IceTcpCandidateType::from(v) as i32), + } + } +} + +impl From for proto::CertificateStats { + fn from(value: rtc::CertificateStats) -> Self { + Self { + fingerprint: value.fingerprint, + fingerprint_algorithm: value.fingerprint_algorithm, + base64_certificate: value.base64_certificate, + issuer_certificate_id: value.issuer_certificate_id, + } + } +} diff --git a/livekit-ffi/src/livekit.proto.rs b/livekit-ffi/src/livekit.proto.rs index 639cafb8..3796339c 100644 --- a/livekit-ffi/src/livekit.proto.rs +++ b/livekit-ffi/src/livekit.proto.rs @@ -2226,4 +2226,977 @@ pub struct DisposeCallback { #[prost(uint64, tag="1")] pub async_id: u64, } +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct RtcStats { + #[prost(oneof="rtc_stats::Stats", tags="3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17")] + pub stats: ::core::option::Option, +} +/// Nested message and enum types in `RtcStats`. +pub mod rtc_stats { + #[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] + pub struct Codec { + #[prost(message, optional, tag="1")] + pub rtc: ::core::option::Option, + #[prost(message, optional, tag="2")] + pub codec: ::core::option::Option, + } + #[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] + pub struct InboundRtp { + #[prost(message, optional, tag="1")] + pub rtc: ::core::option::Option, + #[prost(message, optional, tag="2")] + pub stream: ::core::option::Option, + #[prost(message, optional, tag="3")] + pub received: ::core::option::Option, + #[prost(message, optional, tag="4")] + pub inbound: ::core::option::Option, + } + #[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] + pub struct OutboundRtp { + #[prost(message, optional, tag="1")] + pub rtc: ::core::option::Option, + #[prost(message, optional, tag="2")] + pub stream: ::core::option::Option, + #[prost(message, optional, tag="3")] + pub sent: ::core::option::Option, + #[prost(message, optional, tag="4")] + pub outbound: ::core::option::Option, + } + #[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] + pub struct RemoteInboundRtp { + #[prost(message, optional, tag="1")] + pub rtc: ::core::option::Option, + #[prost(message, optional, tag="2")] + pub stream: ::core::option::Option, + #[prost(message, optional, tag="3")] + pub received: ::core::option::Option, + #[prost(message, optional, tag="4")] + pub remote_inbound: ::core::option::Option, + } + #[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] + pub struct RemoteOutboundRtp { + #[prost(message, optional, tag="1")] + pub rtc: ::core::option::Option, + #[prost(message, optional, tag="2")] + pub stream: ::core::option::Option, + #[prost(message, optional, tag="3")] + pub sent: ::core::option::Option, + #[prost(message, optional, tag="4")] + pub remote_outbound: ::core::option::Option, + } + #[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] + pub struct MediaSource { + #[prost(message, optional, tag="1")] + pub rtc: ::core::option::Option, + #[prost(message, optional, tag="2")] + pub source: ::core::option::Option, + #[prost(message, optional, tag="3")] + pub audio: ::core::option::Option, + #[prost(message, optional, tag="4")] + pub video: ::core::option::Option, + } + #[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] + pub struct MediaPlayout { + #[prost(message, optional, tag="1")] + pub rtc: ::core::option::Option, + #[prost(message, optional, tag="2")] + pub audio_playout: ::core::option::Option, + } + #[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] + pub struct PeerConnection { + #[prost(message, optional, tag="1")] + pub rtc: ::core::option::Option, + #[prost(message, optional, tag="2")] + pub pc: ::core::option::Option, + } + #[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] + pub struct DataChannel { + #[prost(message, optional, tag="1")] + pub rtc: ::core::option::Option, + #[prost(message, optional, tag="2")] + pub dc: ::core::option::Option, + } + #[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] + pub struct Transport { + #[prost(message, optional, tag="1")] + pub rtc: ::core::option::Option, + #[prost(message, optional, tag="2")] + pub transport: ::core::option::Option, + } + #[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] + pub struct CandidatePair { + #[prost(message, optional, tag="1")] + pub rtc: ::core::option::Option, + #[prost(message, optional, tag="2")] + pub candidate_pair: ::core::option::Option, + } + #[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] + pub struct LocalCandidate { + #[prost(message, optional, tag="1")] + pub rtc: ::core::option::Option, + #[prost(message, optional, tag="2")] + pub candidate: ::core::option::Option, + } + #[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] + pub struct RemoteCandidate { + #[prost(message, optional, tag="1")] + pub rtc: ::core::option::Option, + #[prost(message, optional, tag="2")] + pub candidate: ::core::option::Option, + } + #[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] + pub struct Certificate { + #[prost(message, optional, tag="1")] + pub rtc: ::core::option::Option, + #[prost(message, optional, tag="2")] + pub certificate: ::core::option::Option, + } + /// Deprecated + #[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] + pub struct Track { + } + #[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Oneof)] + pub enum Stats { + #[prost(message, tag="3")] + Codec(Codec), + #[prost(message, tag="4")] + InboundRtp(InboundRtp), + #[prost(message, tag="5")] + OutboundRtp(OutboundRtp), + #[prost(message, tag="6")] + RemoteInboundRtp(RemoteInboundRtp), + #[prost(message, tag="7")] + RemoteOutboundRtp(RemoteOutboundRtp), + #[prost(message, tag="8")] + MediaSource(MediaSource), + #[prost(message, tag="9")] + MediaPlayout(MediaPlayout), + #[prost(message, tag="10")] + PeerConnection(PeerConnection), + #[prost(message, tag="11")] + DataChannel(DataChannel), + #[prost(message, tag="12")] + Transport(Transport), + #[prost(message, tag="13")] + CandidatePair(CandidatePair), + #[prost(message, tag="14")] + LocalCandidate(LocalCandidate), + #[prost(message, tag="15")] + RemoteCandidate(RemoteCandidate), + #[prost(message, tag="16")] + Certificate(Certificate), + #[prost(message, tag="17")] + Track(Track), + } +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct RtcStatsData { + #[prost(string, tag="1")] + pub id: ::prost::alloc::string::String, + #[prost(int64, tag="2")] + pub timestamp: i64, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct CodecStats { + #[prost(uint32, tag="1")] + pub payload_type: u32, + #[prost(string, tag="2")] + pub transport_id: ::prost::alloc::string::String, + #[prost(string, tag="3")] + pub mime_type: ::prost::alloc::string::String, + #[prost(uint32, tag="4")] + pub clock_rate: u32, + #[prost(uint32, tag="5")] + pub channels: u32, + #[prost(string, tag="6")] + pub sdp_fmtp_line: ::prost::alloc::string::String, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct RtpStreamStats { + #[prost(uint32, tag="1")] + pub ssrc: u32, + #[prost(string, tag="2")] + pub kind: ::prost::alloc::string::String, + #[prost(string, tag="3")] + pub transport_id: ::prost::alloc::string::String, + #[prost(string, tag="4")] + pub codec_id: ::prost::alloc::string::String, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ReceivedRtpStreamStats { + #[prost(uint64, tag="1")] + pub packets_received: u64, + #[prost(int64, tag="2")] + pub packets_lost: i64, + #[prost(double, tag="3")] + pub jitter: f64, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct InboundRtpStreamStats { + #[prost(string, tag="1")] + pub track_identifier: ::prost::alloc::string::String, + #[prost(string, tag="2")] + pub mid: ::prost::alloc::string::String, + #[prost(string, tag="3")] + pub remote_id: ::prost::alloc::string::String, + #[prost(uint32, tag="4")] + pub frames_decoded: u32, + #[prost(uint32, tag="5")] + pub key_frames_decoded: u32, + #[prost(uint32, tag="6")] + pub frames_rendered: u32, + #[prost(uint32, tag="7")] + pub frames_dropped: u32, + #[prost(uint32, tag="8")] + pub frame_width: u32, + #[prost(uint32, tag="9")] + pub frame_height: u32, + #[prost(double, tag="10")] + pub frames_per_second: f64, + #[prost(uint64, tag="11")] + pub qp_sum: u64, + #[prost(double, tag="12")] + pub total_decode_time: f64, + #[prost(double, tag="13")] + pub total_inter_frame_delay: f64, + #[prost(double, tag="14")] + pub total_squared_inter_frame_delay: f64, + #[prost(uint32, tag="15")] + pub pause_count: u32, + #[prost(double, tag="16")] + pub total_pause_duration: f64, + #[prost(uint32, tag="17")] + pub freeze_count: u32, + #[prost(double, tag="18")] + pub total_freeze_duration: f64, + #[prost(double, tag="19")] + pub last_packet_received_timestamp: f64, + #[prost(uint64, tag="20")] + pub header_bytes_received: u64, + #[prost(uint64, tag="21")] + pub packets_discarded: u64, + #[prost(uint64, tag="22")] + pub fec_bytes_received: u64, + #[prost(uint64, tag="23")] + pub fec_packets_received: u64, + #[prost(uint64, tag="24")] + pub fec_packets_discarded: u64, + #[prost(uint64, tag="25")] + pub bytes_received: u64, + #[prost(uint32, tag="26")] + pub nack_count: u32, + #[prost(uint32, tag="27")] + pub fir_count: u32, + #[prost(uint32, tag="28")] + pub pli_count: u32, + #[prost(double, tag="29")] + pub total_processing_delay: f64, + #[prost(double, tag="30")] + pub estimated_playout_timestamp: f64, + #[prost(double, tag="31")] + pub jitter_buffer_delay: f64, + #[prost(double, tag="32")] + pub jitter_buffer_target_delay: f64, + #[prost(uint64, tag="33")] + pub jitter_buffer_emitted_count: u64, + #[prost(double, tag="34")] + pub jitter_buffer_minimum_delay: f64, + #[prost(uint64, tag="35")] + pub total_samples_received: u64, + #[prost(uint64, tag="36")] + pub concealed_samples: u64, + #[prost(uint64, tag="37")] + pub silent_concealed_samples: u64, + #[prost(uint64, tag="38")] + pub concealment_events: u64, + #[prost(uint64, tag="39")] + pub inserted_samples_for_deceleration: u64, + #[prost(uint64, tag="40")] + pub removed_samples_for_acceleration: u64, + #[prost(double, tag="41")] + pub audio_level: f64, + #[prost(double, tag="42")] + pub total_audio_energy: f64, + #[prost(double, tag="43")] + pub total_samples_duration: f64, + #[prost(uint64, tag="44")] + pub frames_received: u64, + #[prost(string, tag="45")] + pub decoder_implementation: ::prost::alloc::string::String, + #[prost(string, tag="46")] + pub playout_id: ::prost::alloc::string::String, + #[prost(bool, tag="47")] + pub power_efficient_decoder: bool, + #[prost(uint64, tag="48")] + pub frames_assembled_from_multiple_packets: u64, + #[prost(double, tag="49")] + pub total_assembly_time: f64, + #[prost(uint64, tag="50")] + pub retransmitted_packets_received: u64, + #[prost(uint64, tag="51")] + pub retransmitted_bytes_received: u64, + #[prost(uint32, tag="52")] + pub rtx_ssrc: u32, + #[prost(uint32, tag="53")] + pub fec_ssrc: u32, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct SentRtpStreamStats { + #[prost(uint64, tag="1")] + pub packets_sent: u64, + #[prost(uint64, tag="2")] + pub bytes_sent: u64, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct OutboundRtpStreamStats { + #[prost(string, tag="1")] + pub mid: ::prost::alloc::string::String, + #[prost(string, tag="2")] + pub media_source_id: ::prost::alloc::string::String, + #[prost(string, tag="3")] + pub remote_id: ::prost::alloc::string::String, + #[prost(string, tag="4")] + pub rid: ::prost::alloc::string::String, + #[prost(uint64, tag="5")] + pub header_bytes_sent: u64, + #[prost(uint64, tag="6")] + pub retransmitted_packets_sent: u64, + #[prost(uint64, tag="7")] + pub retransmitted_bytes_sent: u64, + #[prost(uint32, tag="8")] + pub rtx_ssrc: u32, + #[prost(double, tag="9")] + pub target_bitrate: f64, + #[prost(uint64, tag="10")] + pub total_encoded_bytes_target: u64, + #[prost(uint32, tag="11")] + pub frame_width: u32, + #[prost(uint32, tag="12")] + pub frame_height: u32, + #[prost(double, tag="13")] + pub frames_per_second: f64, + #[prost(uint32, tag="14")] + pub frames_sent: u32, + #[prost(uint32, tag="15")] + pub huge_frames_sent: u32, + #[prost(uint32, tag="16")] + pub frames_encoded: u32, + #[prost(uint32, tag="17")] + pub key_frames_encoded: u32, + #[prost(uint64, tag="18")] + pub qp_sum: u64, + #[prost(double, tag="19")] + pub total_encode_time: f64, + #[prost(double, tag="20")] + pub total_packet_send_delay: f64, + #[prost(enumeration="QualityLimitationReason", tag="21")] + pub quality_limitation_reason: i32, + #[prost(map="string, double", tag="22")] + pub quality_limitation_durations: ::std::collections::HashMap<::prost::alloc::string::String, f64>, + #[prost(uint32, tag="23")] + pub quality_limitation_resolution_changes: u32, + #[prost(uint32, tag="24")] + pub nack_count: u32, + #[prost(uint32, tag="25")] + pub fir_count: u32, + #[prost(uint32, tag="26")] + pub pli_count: u32, + #[prost(string, tag="27")] + pub encoder_implementation: ::prost::alloc::string::String, + #[prost(bool, tag="28")] + pub power_efficient_encoder: bool, + #[prost(bool, tag="29")] + pub active: bool, + #[prost(string, tag="30")] + pub scalibility_mode: ::prost::alloc::string::String, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct RemoteInboundRtpStreamStats { + #[prost(string, tag="1")] + pub local_id: ::prost::alloc::string::String, + #[prost(double, tag="2")] + pub round_trip_time: f64, + #[prost(double, tag="3")] + pub total_round_trip_time: f64, + #[prost(double, tag="4")] + pub fraction_lost: f64, + #[prost(uint64, tag="5")] + pub round_trip_time_measurements: u64, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct RemoteOutboundRtpStreamStats { + #[prost(string, tag="1")] + pub local_id: ::prost::alloc::string::String, + #[prost(double, tag="2")] + pub remote_timestamp: f64, + #[prost(uint64, tag="3")] + pub reports_sent: u64, + #[prost(double, tag="4")] + pub round_trip_time: f64, + #[prost(double, tag="5")] + pub total_round_trip_time: f64, + #[prost(uint64, tag="6")] + pub round_trip_time_measurements: u64, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct MediaSourceStats { + #[prost(string, tag="1")] + pub track_identifier: ::prost::alloc::string::String, + #[prost(string, tag="2")] + pub kind: ::prost::alloc::string::String, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct AudioSourceStats { + #[prost(double, tag="1")] + pub audio_level: f64, + #[prost(double, tag="2")] + pub total_audio_energy: f64, + #[prost(double, tag="3")] + pub total_samples_duration: f64, + #[prost(double, tag="4")] + pub echo_return_loss: f64, + #[prost(double, tag="5")] + pub echo_return_loss_enhancement: f64, + #[prost(double, tag="6")] + pub dropped_samples_duration: f64, + #[prost(uint32, tag="7")] + pub dropped_samples_events: u32, + #[prost(double, tag="8")] + pub total_capture_delay: f64, + #[prost(uint64, tag="9")] + pub total_samples_captured: u64, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct VideoSourceStats { + #[prost(uint32, tag="1")] + pub width: u32, + #[prost(uint32, tag="2")] + pub height: u32, + #[prost(uint32, tag="3")] + pub frames: u32, + #[prost(double, tag="4")] + pub frames_per_second: f64, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct AudioPlayoutStats { + #[prost(string, tag="1")] + pub kind: ::prost::alloc::string::String, + #[prost(double, tag="2")] + pub synthesized_samples_duration: f64, + #[prost(uint32, tag="3")] + pub synthesized_samples_events: u32, + #[prost(double, tag="4")] + pub total_samples_duration: f64, + #[prost(double, tag="5")] + pub total_playout_delay: f64, + #[prost(uint64, tag="6")] + pub total_samples_count: u64, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct PeerConnectionStats { + #[prost(uint32, tag="1")] + pub data_channels_opened: u32, + #[prost(uint32, tag="2")] + pub data_channels_closed: u32, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct DataChannelStats { + #[prost(string, tag="1")] + pub label: ::prost::alloc::string::String, + #[prost(string, tag="2")] + pub protocol: ::prost::alloc::string::String, + #[prost(int32, tag="3")] + pub data_channel_identifier: i32, + #[prost(enumeration="DataChannelState", optional, tag="4")] + pub state: ::core::option::Option, + #[prost(uint32, tag="5")] + pub messages_sent: u32, + #[prost(uint64, tag="6")] + pub bytes_sent: u64, + #[prost(uint32, tag="7")] + pub messages_received: u32, + #[prost(uint64, tag="8")] + pub bytes_received: u64, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct TransportStats { + #[prost(uint64, tag="1")] + pub packets_sent: u64, + #[prost(uint64, tag="2")] + pub packets_received: u64, + #[prost(uint64, tag="3")] + pub bytes_sent: u64, + #[prost(uint64, tag="4")] + pub bytes_received: u64, + #[prost(enumeration="IceRole", tag="5")] + pub ice_role: i32, + #[prost(string, tag="6")] + pub ice_local_username_fragment: ::prost::alloc::string::String, + #[prost(enumeration="DtlsTransportState", optional, tag="7")] + pub dtls_state: ::core::option::Option, + #[prost(enumeration="IceTransportState", optional, tag="8")] + pub ice_state: ::core::option::Option, + #[prost(string, tag="9")] + pub selected_candidate_pair_id: ::prost::alloc::string::String, + #[prost(string, tag="10")] + pub local_certificate_id: ::prost::alloc::string::String, + #[prost(string, tag="11")] + pub remote_certificate_id: ::prost::alloc::string::String, + #[prost(string, tag="12")] + pub tls_version: ::prost::alloc::string::String, + #[prost(string, tag="13")] + pub dtls_cipher: ::prost::alloc::string::String, + #[prost(enumeration="DtlsRole", tag="14")] + pub dtls_role: i32, + #[prost(string, tag="15")] + pub srtp_cipher: ::prost::alloc::string::String, + #[prost(uint32, tag="16")] + pub selected_candidate_pair_changes: u32, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct CandidatePairStats { + #[prost(string, tag="1")] + pub transport_id: ::prost::alloc::string::String, + #[prost(string, tag="2")] + pub local_candidate_id: ::prost::alloc::string::String, + #[prost(string, tag="3")] + pub remote_candidate_id: ::prost::alloc::string::String, + #[prost(enumeration="IceCandidatePairState", optional, tag="4")] + pub state: ::core::option::Option, + #[prost(bool, tag="5")] + pub nominated: bool, + #[prost(uint64, tag="6")] + pub packets_sent: u64, + #[prost(uint64, tag="7")] + pub packets_received: u64, + #[prost(uint64, tag="8")] + pub bytes_sent: u64, + #[prost(uint64, tag="9")] + pub bytes_received: u64, + #[prost(double, tag="10")] + pub last_packet_sent_timestamp: f64, + #[prost(double, tag="11")] + pub last_packet_received_timestamp: f64, + #[prost(double, tag="12")] + pub total_round_trip_time: f64, + #[prost(double, tag="13")] + pub current_round_trip_time: f64, + #[prost(double, tag="14")] + pub available_outgoing_bitrate: f64, + #[prost(double, tag="15")] + pub available_incoming_bitrate: f64, + #[prost(uint64, tag="16")] + pub requests_received: u64, + #[prost(uint64, tag="17")] + pub requests_sent: u64, + #[prost(uint64, tag="18")] + pub responses_received: u64, + #[prost(uint64, tag="19")] + pub responses_sent: u64, + #[prost(uint64, tag="20")] + pub consent_requests_sent: u64, + #[prost(uint32, tag="21")] + pub packets_discarded_on_send: u32, + #[prost(uint64, tag="22")] + pub bytes_discarded_on_send: u64, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct IceCandidateStats { + #[prost(string, tag="1")] + pub transport_id: ::prost::alloc::string::String, + #[prost(string, tag="2")] + pub address: ::prost::alloc::string::String, + #[prost(int32, tag="3")] + pub port: i32, + #[prost(string, tag="4")] + pub protocol: ::prost::alloc::string::String, + #[prost(enumeration="IceCandidateType", optional, tag="5")] + pub candidate_type: ::core::option::Option, + #[prost(int32, tag="6")] + pub priority: i32, + #[prost(string, tag="7")] + pub url: ::prost::alloc::string::String, + #[prost(enumeration="IceServerTransportProtocol", optional, tag="8")] + pub relay_protocol: ::core::option::Option, + #[prost(string, tag="9")] + pub foundation: ::prost::alloc::string::String, + #[prost(string, tag="10")] + pub related_address: ::prost::alloc::string::String, + #[prost(int32, tag="11")] + pub related_port: i32, + #[prost(string, tag="12")] + pub username_fragment: ::prost::alloc::string::String, + #[prost(enumeration="IceTcpCandidateType", optional, tag="13")] + pub tcp_type: ::core::option::Option, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct CertificateStats { + #[prost(string, tag="1")] + pub fingerprint: ::prost::alloc::string::String, + #[prost(string, tag="2")] + pub fingerprint_algorithm: ::prost::alloc::string::String, + #[prost(string, tag="3")] + pub base64_certificate: ::prost::alloc::string::String, + #[prost(string, tag="4")] + pub issuer_certificate_id: ::prost::alloc::string::String, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum DataChannelState { + DcConnecting = 0, + DcOpen = 1, + DcClosing = 2, + DcClosed = 3, +} +impl DataChannelState { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + DataChannelState::DcConnecting => "DC_CONNECTING", + DataChannelState::DcOpen => "DC_OPEN", + DataChannelState::DcClosing => "DC_CLOSING", + DataChannelState::DcClosed => "DC_CLOSED", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "DC_CONNECTING" => Some(Self::DcConnecting), + "DC_OPEN" => Some(Self::DcOpen), + "DC_CLOSING" => Some(Self::DcClosing), + "DC_CLOSED" => Some(Self::DcClosed), + _ => None, + } + } +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum QualityLimitationReason { + LimitationNone = 0, + LimitationCpu = 1, + LimitationBandwidth = 2, + LimitationOther = 3, +} +impl QualityLimitationReason { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + QualityLimitationReason::LimitationNone => "LIMITATION_NONE", + QualityLimitationReason::LimitationCpu => "LIMITATION_CPU", + QualityLimitationReason::LimitationBandwidth => "LIMITATION_BANDWIDTH", + QualityLimitationReason::LimitationOther => "LIMITATION_OTHER", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "LIMITATION_NONE" => Some(Self::LimitationNone), + "LIMITATION_CPU" => Some(Self::LimitationCpu), + "LIMITATION_BANDWIDTH" => Some(Self::LimitationBandwidth), + "LIMITATION_OTHER" => Some(Self::LimitationOther), + _ => None, + } + } +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum IceRole { + IceUnknown = 0, + IceControlling = 1, + IceControlled = 2, +} +impl IceRole { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + IceRole::IceUnknown => "ICE_UNKNOWN", + IceRole::IceControlling => "ICE_CONTROLLING", + IceRole::IceControlled => "ICE_CONTROLLED", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "ICE_UNKNOWN" => Some(Self::IceUnknown), + "ICE_CONTROLLING" => Some(Self::IceControlling), + "ICE_CONTROLLED" => Some(Self::IceControlled), + _ => None, + } + } +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum DtlsTransportState { + DtlsTransportNew = 0, + DtlsTransportConnecting = 1, + DtlsTransportConnected = 2, + DtlsTransportClosed = 3, + DtlsTransportFailed = 4, +} +impl DtlsTransportState { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + DtlsTransportState::DtlsTransportNew => "DTLS_TRANSPORT_NEW", + DtlsTransportState::DtlsTransportConnecting => "DTLS_TRANSPORT_CONNECTING", + DtlsTransportState::DtlsTransportConnected => "DTLS_TRANSPORT_CONNECTED", + DtlsTransportState::DtlsTransportClosed => "DTLS_TRANSPORT_CLOSED", + DtlsTransportState::DtlsTransportFailed => "DTLS_TRANSPORT_FAILED", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "DTLS_TRANSPORT_NEW" => Some(Self::DtlsTransportNew), + "DTLS_TRANSPORT_CONNECTING" => Some(Self::DtlsTransportConnecting), + "DTLS_TRANSPORT_CONNECTED" => Some(Self::DtlsTransportConnected), + "DTLS_TRANSPORT_CLOSED" => Some(Self::DtlsTransportClosed), + "DTLS_TRANSPORT_FAILED" => Some(Self::DtlsTransportFailed), + _ => None, + } + } +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum IceTransportState { + IceTransportNew = 0, + IceTransportChecking = 1, + IceTransportConnected = 2, + IceTransportCompleted = 3, + IceTransportDisconnected = 4, + IceTransportFailed = 5, + IceTransportClosed = 6, +} +impl IceTransportState { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + IceTransportState::IceTransportNew => "ICE_TRANSPORT_NEW", + IceTransportState::IceTransportChecking => "ICE_TRANSPORT_CHECKING", + IceTransportState::IceTransportConnected => "ICE_TRANSPORT_CONNECTED", + IceTransportState::IceTransportCompleted => "ICE_TRANSPORT_COMPLETED", + IceTransportState::IceTransportDisconnected => "ICE_TRANSPORT_DISCONNECTED", + IceTransportState::IceTransportFailed => "ICE_TRANSPORT_FAILED", + IceTransportState::IceTransportClosed => "ICE_TRANSPORT_CLOSED", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "ICE_TRANSPORT_NEW" => Some(Self::IceTransportNew), + "ICE_TRANSPORT_CHECKING" => Some(Self::IceTransportChecking), + "ICE_TRANSPORT_CONNECTED" => Some(Self::IceTransportConnected), + "ICE_TRANSPORT_COMPLETED" => Some(Self::IceTransportCompleted), + "ICE_TRANSPORT_DISCONNECTED" => Some(Self::IceTransportDisconnected), + "ICE_TRANSPORT_FAILED" => Some(Self::IceTransportFailed), + "ICE_TRANSPORT_CLOSED" => Some(Self::IceTransportClosed), + _ => None, + } + } +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum DtlsRole { + DtlsClient = 0, + DtlsServer = 1, + DtlsUnknown = 2, +} +impl DtlsRole { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + DtlsRole::DtlsClient => "DTLS_CLIENT", + DtlsRole::DtlsServer => "DTLS_SERVER", + DtlsRole::DtlsUnknown => "DTLS_UNKNOWN", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "DTLS_CLIENT" => Some(Self::DtlsClient), + "DTLS_SERVER" => Some(Self::DtlsServer), + "DTLS_UNKNOWN" => Some(Self::DtlsUnknown), + _ => None, + } + } +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum IceCandidatePairState { + PairFrozen = 0, + PairWaiting = 1, + PairInProgress = 2, + PairFailed = 3, + PairSucceeded = 4, +} +impl IceCandidatePairState { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + IceCandidatePairState::PairFrozen => "PAIR_FROZEN", + IceCandidatePairState::PairWaiting => "PAIR_WAITING", + IceCandidatePairState::PairInProgress => "PAIR_IN_PROGRESS", + IceCandidatePairState::PairFailed => "PAIR_FAILED", + IceCandidatePairState::PairSucceeded => "PAIR_SUCCEEDED", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "PAIR_FROZEN" => Some(Self::PairFrozen), + "PAIR_WAITING" => Some(Self::PairWaiting), + "PAIR_IN_PROGRESS" => Some(Self::PairInProgress), + "PAIR_FAILED" => Some(Self::PairFailed), + "PAIR_SUCCEEDED" => Some(Self::PairSucceeded), + _ => None, + } + } +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum IceCandidateType { + Host = 0, + Srflx = 1, + Prflx = 2, + Relay = 3, +} +impl IceCandidateType { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + IceCandidateType::Host => "HOST", + IceCandidateType::Srflx => "SRFLX", + IceCandidateType::Prflx => "PRFLX", + IceCandidateType::Relay => "RELAY", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "HOST" => Some(Self::Host), + "SRFLX" => Some(Self::Srflx), + "PRFLX" => Some(Self::Prflx), + "RELAY" => Some(Self::Relay), + _ => None, + } + } +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum IceServerTransportProtocol { + TransportUdp = 0, + TransportTcp = 1, + TransportTls = 2, +} +impl IceServerTransportProtocol { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + IceServerTransportProtocol::TransportUdp => "TRANSPORT_UDP", + IceServerTransportProtocol::TransportTcp => "TRANSPORT_TCP", + IceServerTransportProtocol::TransportTls => "TRANSPORT_TLS", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "TRANSPORT_UDP" => Some(Self::TransportUdp), + "TRANSPORT_TCP" => Some(Self::TransportTcp), + "TRANSPORT_TLS" => Some(Self::TransportTls), + _ => None, + } + } +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum IceTcpCandidateType { + CandidateActive = 0, + CandidatePassive = 1, + CandidateSo = 2, +} +impl IceTcpCandidateType { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + IceTcpCandidateType::CandidateActive => "CANDIDATE_ACTIVE", + IceTcpCandidateType::CandidatePassive => "CANDIDATE_PASSIVE", + IceTcpCandidateType::CandidateSo => "CANDIDATE_SO", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "CANDIDATE_ACTIVE" => Some(Self::CandidateActive), + "CANDIDATE_PASSIVE" => Some(Self::CandidatePassive), + "CANDIDATE_SO" => Some(Self::CandidateSo), + _ => None, + } + } +} // @@protoc_insertion_point(module) diff --git a/livekit/src/room/mod.rs b/livekit/src/room/mod.rs index 47f43e4f..783c4fcd 100644 --- a/livekit/src/room/mod.rs +++ b/livekit/src/room/mod.rs @@ -23,6 +23,7 @@ use libwebrtc::prelude::{ ContinualGatheringPolicy, IceTransportsType, MediaStream, MediaStreamTrack, RtcConfiguration, }; use libwebrtc::rtp_transceiver::RtpTransceiver; +use libwebrtc::RtcError; use livekit_api::signal_client::SignalOptions; use livekit_protocol as proto; use livekit_protocol::observer::Dispatcher; @@ -55,6 +56,8 @@ pub enum RoomError { Engine(#[from] EngineError), #[error("room failure: {0}")] Internal(String), + #[error("rtc: {0}")] + Rtc(#[from] RtcError), #[error("this track or a track of the same source is already published")] TrackAlreadyPublished, #[error("already closed")] @@ -474,7 +477,6 @@ pub(crate) struct RoomSession { local_participant: LocalParticipant, participants: RwLock<( // Keep track of participants by sid and identity - // Ideally we would just need identity HashMap, HashMap, )>, diff --git a/livekit/src/room/participant/local_participant.rs b/livekit/src/room/participant/local_participant.rs index f5669473..6595f04b 100644 --- a/livekit/src/room/participant/local_participant.rs +++ b/livekit/src/room/participant/local_participant.rs @@ -268,7 +268,7 @@ impl LocalParticipant { let track = publication.track().unwrap(); let sender = track.transceiver().unwrap().sender(); - self.inner.rtc_engine.remove_track(sender).await?; + self.inner.rtc_engine.remove_track(sender)?; track.set_transceiver(None); if let Some(local_track_unpublished) = diff --git a/livekit/src/room/track/local_audio_track.rs b/livekit/src/room/track/local_audio_track.rs index 26cd9068..d3990e51 100644 --- a/livekit/src/room/track/local_audio_track.rs +++ b/livekit/src/room/track/local_audio_track.rs @@ -17,6 +17,7 @@ use crate::prelude::*; use crate::rtc_engine::lk_runtime::LkRuntime; use core::panic; use libwebrtc::prelude::*; +use libwebrtc::stats::RtcStats; use livekit_protocol as proto; use std::fmt::Debug; use std::sync::Arc; @@ -119,11 +120,15 @@ impl LocalAudioTrack { false } - pub fn on_muted(&self, f: impl Fn(Track) + Send + 'static) { + pub async fn get_stats(&self) -> RoomResult> { + super::get_stats(&self.inner).await + } + + pub(crate) fn on_muted(&self, f: impl Fn(Track) + Send + 'static) { *self.inner.events.muted.lock() = Some(Box::new(f)); } - pub fn on_unmuted(&self, f: impl Fn(Track) + Send + 'static) { + pub(crate) fn on_unmuted(&self, f: impl Fn(Track) + Send + 'static) { *self.inner.events.unmuted.lock() = Some(Box::new(f)); } diff --git a/livekit/src/room/track/local_video_track.rs b/livekit/src/room/track/local_video_track.rs index 5245c243..3bd8abab 100644 --- a/livekit/src/room/track/local_video_track.rs +++ b/livekit/src/room/track/local_video_track.rs @@ -16,6 +16,7 @@ use super::TrackInner; use crate::prelude::*; use crate::rtc_engine::lk_runtime::LkRuntime; use libwebrtc::prelude::*; +use libwebrtc::stats::RtcStats; use livekit_protocol as proto; use std::fmt::Debug; use std::sync::Arc; @@ -119,11 +120,15 @@ impl LocalVideoTrack { self.source.clone() } - pub fn on_muted(&self, f: impl Fn(Track) + Send + 'static) { + pub async fn get_stats(&self) -> RoomResult> { + super::get_stats(&self.inner).await + } + + pub(crate) fn on_muted(&self, f: impl Fn(Track) + Send + 'static) { *self.inner.events.muted.lock() = Some(Box::new(f)); } - pub fn on_unmuted(&self, f: impl Fn(Track) + Send + 'static) { + pub(crate) fn on_unmuted(&self, f: impl Fn(Track) + Send + 'static) { *self.inner.events.unmuted.lock() = Some(Box::new(f)); } diff --git a/livekit/src/room/track/mod.rs b/livekit/src/room/track/mod.rs index d341ff1a..770e86b3 100644 --- a/livekit/src/room/track/mod.rs +++ b/livekit/src/room/track/mod.rs @@ -14,6 +14,7 @@ use crate::prelude::*; use libwebrtc::prelude::*; +use libwebrtc::stats::RtcStats; use livekit_protocol as proto; use livekit_protocol::enum_dispatch; use parking_lot::{Mutex, RwLock}; @@ -111,6 +112,15 @@ impl Track { Self::RemoteVideo(track) => track.rtc_track().into(), } } + + pub async fn get_stats(&self) -> RoomResult> { + match self { + Self::LocalAudio(track) => track.get_stats().await, + Self::LocalVideo(track) => track.get_stats().await, + Self::RemoteAudio(track) => track.get_stats().await, + Self::RemoteVideo(track) => track.get_stats().await, + } + } } pub(super) use track_dispatch; @@ -163,6 +173,16 @@ pub(super) fn new_inner( } } +pub(super) async fn get_stats(inner: &Arc) -> RoomResult> { + let transceiver = inner.info.read().transceiver.clone(); + let Some(transceiver) = transceiver.as_ref() else { + return Err(RoomError::Internal("no transceiver found for track".into())); + }; + + let rtp_receiver = transceiver.receiver(); + Ok(rtp_receiver.get_stats().await?) +} + /// This is only called for local tracks pub(super) fn set_muted(inner: &Arc, track: &Track, muted: bool) { let info = inner.info.read(); diff --git a/livekit/src/room/track/remote_audio_track.rs b/livekit/src/room/track/remote_audio_track.rs index 2a1119ce..9672615b 100644 --- a/livekit/src/room/track/remote_audio_track.rs +++ b/livekit/src/room/track/remote_audio_track.rs @@ -15,6 +15,7 @@ use super::{remote_track, TrackInner}; use crate::prelude::*; use libwebrtc::prelude::*; +use libwebrtc::stats::RtcStats; use livekit_protocol as proto; use std::fmt::Debug; use std::sync::Arc; @@ -89,11 +90,15 @@ impl RemoteAudioTrack { true } - pub fn on_muted(&self, f: impl Fn(Track) + Send + 'static) { + pub async fn get_stats(&self) -> RoomResult> { + super::get_stats(&self.inner).await + } + + pub(crate) fn on_muted(&self, f: impl Fn(Track) + Send + 'static) { *self.inner.events.muted.lock() = Some(Box::new(f)); } - pub fn on_unmuted(&self, f: impl Fn(Track) + Send + 'static) { + pub(crate) fn on_unmuted(&self, f: impl Fn(Track) + Send + 'static) { *self.inner.events.unmuted.lock() = Some(Box::new(f)); } diff --git a/livekit/src/room/track/remote_track.rs b/livekit/src/room/track/remote_track.rs index c0bde179..24262536 100644 --- a/livekit/src/room/track/remote_track.rs +++ b/livekit/src/room/track/remote_track.rs @@ -16,6 +16,8 @@ use super::track_dispatch; use super::TrackInner; use crate::prelude::*; use libwebrtc::prelude::*; +use libwebrtc::stats::InboundRtpStats; +use libwebrtc::stats::RtcStats; use livekit_protocol as proto; use livekit_protocol::enum_dispatch; use std::sync::Arc; diff --git a/livekit/src/room/track/remote_video_track.rs b/livekit/src/room/track/remote_video_track.rs index e95b00dc..e146c91a 100644 --- a/livekit/src/room/track/remote_video_track.rs +++ b/livekit/src/room/track/remote_video_track.rs @@ -16,6 +16,7 @@ use super::remote_track; use super::TrackInner; use crate::prelude::*; use libwebrtc::prelude::*; +use libwebrtc::stats::RtcStats; use livekit_protocol as proto; use std::fmt::Debug; use std::sync::Arc; @@ -90,20 +91,22 @@ impl RemoteVideoTrack { true } - pub fn on_muted(&self, f: impl Fn(Track) + Send + 'static) { + pub async fn get_stats(&self) -> RoomResult> { + super::get_stats(&self.inner).await + } + + pub(crate) fn on_muted(&self, f: impl Fn(Track) + Send + 'static) { *self.inner.events.muted.lock() = Some(Box::new(f)); } - pub fn on_unmuted(&self, f: impl Fn(Track) + Send + 'static) { + pub(crate) fn on_unmuted(&self, f: impl Fn(Track) + Send + 'static) { *self.inner.events.unmuted.lock() = Some(Box::new(f)); } - #[allow(dead_code)] pub(crate) fn transceiver(&self) -> Option { self.inner.info.read().transceiver.clone() } - #[allow(dead_code)] pub(crate) fn set_transceiver(&self, transceiver: Option) { self.inner.info.write().transceiver = transceiver; } diff --git a/livekit/src/rtc_engine/mod.rs b/livekit/src/rtc_engine/mod.rs index 8f62e10f..f8b9d417 100644 --- a/livekit/src/rtc_engine/mod.rs +++ b/livekit/src/rtc_engine/mod.rs @@ -136,6 +136,7 @@ struct EngineHandle { struct EngineInner { // Keep a strong reference to LkRuntime to avoid creating a new RtcRuntime or PeerConnection factory accross multiple Rtc sessions + #[allow(dead_code)] lk_runtime: Arc, engine_tx: EngineEmitter, options: EngineOptions, @@ -204,12 +205,12 @@ impl RtcEngine { session.add_track(req).await } - pub async fn remove_track(&self, sender: RtpSender) -> EngineResult<()> { + pub fn remove_track(&self, sender: RtpSender) -> EngineResult<()> { // We don't need to wait for the reconnection let session = self.inner.running_handle.read().session.clone(); - session.remove_track(sender).await // TODO(theomonnom): Ignore errors where this - // RtpSender is bound to the old session. (Can - // happen on bad timing and it is safe to ignore) + session.remove_track(sender) // TODO(theomonnom): Ignore errors where this + // RtpSender is bound to the old session. (Can + // happen on bad timing and it is safe to ignore) } pub async fn create_sender( diff --git a/livekit/src/rtc_engine/rtc_session.rs b/livekit/src/rtc_engine/rtc_session.rs index db2cc694..49cec8cf 100644 --- a/livekit/src/rtc_engine/rtc_session.rs +++ b/livekit/src/rtc_engine/rtc_session.rs @@ -230,8 +230,8 @@ impl RtcSession { self.inner.add_track(req).await } - pub async fn remove_track(&self, sender: RtpSender) -> EngineResult<()> { - self.inner.remove_track(sender).await + pub fn remove_track(&self, sender: RtpSender) -> EngineResult<()> { + self.inner.remove_track(sender) } pub async fn create_sender( @@ -612,7 +612,7 @@ impl SessionInner { } } - async fn remove_track(&self, sender: RtpSender) -> EngineResult<()> { + fn remove_track(&self, sender: RtpSender) -> EngineResult<()> { if let Some(track) = sender.track() { let mut pending_tracks = self.pending_tracks.lock(); pending_tracks.remove(&track.id()); @@ -902,13 +902,13 @@ impl SessionInner { } let dc = self.data_channel(SignalTarget::Publisher, kind).unwrap(); - if dc.state() == DataState::Open { + if dc.state() == DataChannelState::Open { return Ok(()); } // Wait until the PeerConnection is connected let wait_connected = async { - while !self.publisher_pc.is_connected() || dc.state() != DataState::Open { + while !self.publisher_pc.is_connected() || dc.state() != DataChannelState::Open { if self.closed.load(Ordering::Acquire) { return Err(EngineError::Connection("closed".into())); } diff --git a/webrtc-sys/include/livekit/jsep.h b/webrtc-sys/include/livekit/jsep.h index 826d4ac2..3cb59a0c 100644 --- a/webrtc-sys/include/livekit/jsep.h +++ b/webrtc-sys/include/livekit/jsep.h @@ -22,7 +22,9 @@ #include "api/ref_counted_base.h" #include "api/set_local_description_observer_interface.h" #include "api/set_remote_description_observer_interface.h" +#include "api/stats/rtc_stats_collector_callback.h" #include "livekit/rtc_error.h" +#include "rtc_base/ref_count.h" #include "rust/cxx.h" namespace livekit { @@ -33,7 +35,7 @@ class SessionDescription; namespace livekit { -class AsyncContext; +class PeerContext; class IceCandidate { public: @@ -86,47 +88,64 @@ class NativeCreateSdpObserver : public webrtc::CreateSessionDescriptionObserver { public: NativeCreateSdpObserver( - rust::Box ctx, - rust::Fn ctx, + rust::Box ctx, + rust::Fn ctx, std::unique_ptr)> on_success, - rust::Fn ctx, RtcError)> on_error); + rust::Fn ctx, RtcError)> on_error); void OnSuccess(webrtc::SessionDescriptionInterface* desc) override; void OnFailure(webrtc::RTCError error) override; private: - rust::Box ctx_; - rust::Fn, std::unique_ptr)> + rust::Box ctx_; + rust::Fn, std::unique_ptr)> on_success_; - rust::Fn, RtcError)> on_error_; + rust::Fn, RtcError)> on_error_; }; class NativeSetLocalSdpObserver : public webrtc::SetLocalDescriptionObserverInterface { public: NativeSetLocalSdpObserver( - rust::Box ctx, - rust::Fn, RtcError)> on_complete); + rust::Box ctx, + rust::Fn, RtcError)> on_complete); void OnSetLocalDescriptionComplete(webrtc::RTCError error) override; private: - rust::Box ctx_; - rust::Fn, RtcError)> on_complete_; + rust::Box ctx_; + rust::Fn, RtcError)> on_complete_; }; class NativeSetRemoteSdpObserver : public webrtc::SetRemoteDescriptionObserverInterface { public: NativeSetRemoteSdpObserver( - rust::Box ctx, - rust::Fn, RtcError)> on_complete); + rust::Box ctx, + rust::Fn, RtcError)> on_complete); void OnSetRemoteDescriptionComplete(webrtc::RTCError error) override; private: - rust::Box ctx_; - rust::Fn, RtcError)> on_complete_; + rust::Box ctx_; + rust::Fn, RtcError)> on_complete_; }; + +template // Context type +class NativeRtcStatsCollector : public webrtc::RTCStatsCollectorCallback { + public: + NativeRtcStatsCollector(rust::Box ctx, + rust::Fn, rust::String)> on_stats); + + void OnStatsDelivered( + const rtc::scoped_refptr &report) override; + + private: + rust::Box ctx_; + rust::Fn, rust::String)> on_stats_; +}; + + + } // namespace livekit diff --git a/webrtc-sys/include/livekit/peer_connection.h b/webrtc-sys/include/livekit/peer_connection.h index a6988184..67e71cba 100644 --- a/webrtc-sys/include/livekit/peer_connection.h +++ b/webrtc-sys/include/livekit/peer_connection.h @@ -19,6 +19,7 @@ #include #include "api/peer_connection_interface.h" +#include "api/scoped_refptr.h" #include "livekit/data_channel.h" #include "livekit/helper.h" #include "livekit/jsep.h" @@ -37,54 +38,58 @@ class NativePeerConnectionObserver; } // namespace livekit #include "webrtc-sys/src/peer_connection.rs.h" +#include "livekit/peer_connection_factory.h" + namespace livekit { webrtc::PeerConnectionInterface::RTCConfiguration to_native_rtc_configuration( RtcConfiguration config); class PeerConnectionFactory; -class PeerConnection { +class PeerConnection : webrtc::PeerConnectionObserver { public: PeerConnection( std::shared_ptr rtc_runtime, - std::unique_ptr observer, - rtc::scoped_refptr peer_connection); + rtc::scoped_refptr pc_factory, + rust::Box observer); ~PeerConnection(); + bool Initialize(webrtc::PeerConnectionInterface::RTCConfiguration config); + void set_configuration(RtcConfiguration config) const; void create_offer( RtcOfferAnswerOptions options, - rust::Box ctx, - rust::Fn, + rust::Box ctx, + rust::Fn, std::unique_ptr)> on_success, - rust::Fn, RtcError)> on_error) const; + rust::Fn, RtcError)> on_error) const; void create_answer( RtcOfferAnswerOptions options, - rust::Box ctx, - rust::Fn, + rust::Box ctx, + rust::Fn, std::unique_ptr)> on_success, - rust::Fn, RtcError)> on_error) const; + rust::Fn, RtcError)> on_error) const; void set_local_description( std::unique_ptr desc, - rust::Box ctx, - rust::Fn, RtcError)> on_complete) const; + rust::Box ctx, + rust::Fn, RtcError)> on_complete) const; void set_remote_description( std::unique_ptr desc, - rust::Box ctx, - rust::Fn, RtcError)> on_complete) const; + rust::Box ctx, + rust::Fn, RtcError)> on_complete) const; std::shared_ptr create_data_channel(rust::String label, DataChannelInit init) const; void add_ice_candidate( std::shared_ptr candidate, - rust::Box ctx, - rust::Fn, RtcError)> on_complete) const; + rust::Box ctx, + rust::Fn, RtcError)> on_complete) const; std::shared_ptr add_track( std::shared_ptr track, @@ -92,6 +97,10 @@ class PeerConnection { void remove_track(std::shared_ptr sender) const; + void get_stats( + rust::Box ctx, + rust::Fn, rust::String)> on_stats) const; + void restart_ice() const; std::shared_ptr add_transceiver( @@ -130,23 +139,6 @@ class PeerConnection { void close() const; - private: - std::shared_ptr rtc_runtime_; - std::unique_ptr observer_; - rtc::scoped_refptr peer_connection_; -}; - -static std::shared_ptr _shared_peer_connection() { - return nullptr; // Ignore -} - -class NativePeerConnectionObserver : public webrtc::PeerConnectionObserver { - public: - NativePeerConnectionObserver( - rust::Box observer); - - ~NativePeerConnectionObserver(); - void OnSignalingChange( webrtc::PeerConnectionInterface::SignalingState new_state) override; @@ -205,15 +197,14 @@ class NativePeerConnectionObserver : public webrtc::PeerConnectionObserver { void OnInterestingUsage(int usage_pattern) override; private: - friend PeerConnectionFactory; - // The RtcRuntime is set inside PeerConnectionFactory, we can simplify that - // once create_native_connection_observer is removed std::shared_ptr rtc_runtime_; + rtc::scoped_refptr pc_factory_; rust::Box observer_; + rtc::scoped_refptr peer_connection_; }; -std::unique_ptr -create_native_peer_connection_observer( - rust::Box observer); +static std::shared_ptr _shared_peer_connection() { + return nullptr; // Ignore +} } // namespace livekit diff --git a/webrtc-sys/include/livekit/peer_connection_factory.h b/webrtc-sys/include/livekit/peer_connection_factory.h index f4b351fb..7d22c4e8 100644 --- a/webrtc-sys/include/livekit/peer_connection_factory.h +++ b/webrtc-sys/include/livekit/peer_connection_factory.h @@ -20,18 +20,21 @@ #include "api/scoped_refptr.h" #include "livekit/audio_device.h" #include "media_stream.h" -#include "peer_connection.h" #include "rtp_parameters.h" #include "rust/cxx.h" #include "webrtc.h" namespace livekit { class PeerConnectionFactory; +class PeerConnectionObserverWrapper; } // namespace livekit #include "webrtc-sys/src/peer_connection_factory.rs.h" namespace livekit { +class PeerConnection; +struct RtcConfiguration; + webrtc::PeerConnectionInterface::RTCConfiguration to_native_rtc_configuration( RtcConfiguration config); @@ -42,7 +45,7 @@ class PeerConnectionFactory { std::shared_ptr create_peer_connection( RtcConfiguration config, - std::unique_ptr observer) const; + rust::Box observer) const; std::shared_ptr create_video_track( rust::String label, diff --git a/webrtc-sys/include/livekit/rtp_receiver.h b/webrtc-sys/include/livekit/rtp_receiver.h index 31ff174f..1bec92b8 100644 --- a/webrtc-sys/include/livekit/rtp_receiver.h +++ b/webrtc-sys/include/livekit/rtp_receiver.h @@ -18,7 +18,9 @@ #include +#include "api/peer_connection_interface.h" #include "api/rtp_receiver_interface.h" +#include "api/scoped_refptr.h" #include "livekit/helper.h" #include "livekit/media_stream.h" #include "livekit/rtp_parameters.h" @@ -36,11 +38,17 @@ namespace livekit { // TODO(theomonnom): FrameTransformer & FrameDecryptor interface class RtpReceiver { public: - RtpReceiver(std::shared_ptr rtc_runtime, - rtc::scoped_refptr receiver); + RtpReceiver( + std::shared_ptr rtc_runtime, + rtc::scoped_refptr receiver, + rtc::scoped_refptr peer_connection); std::shared_ptr track() const; + void get_stats( + rust::Box ctx, + rust::Fn, rust::String)> on_stats) const; + rust::Vec stream_ids() const; rust::Vec streams() const; @@ -61,6 +69,7 @@ class RtpReceiver { private: std::shared_ptr rtc_runtime_; rtc::scoped_refptr receiver_; + rtc::scoped_refptr peer_connection_; }; static std::shared_ptr _shared_rtp_receiver() { diff --git a/webrtc-sys/include/livekit/rtp_sender.h b/webrtc-sys/include/livekit/rtp_sender.h index 73c3c884..b9ff44e2 100644 --- a/webrtc-sys/include/livekit/rtp_sender.h +++ b/webrtc-sys/include/livekit/rtp_sender.h @@ -18,7 +18,9 @@ #include +#include "api/peer_connection_interface.h" #include "api/rtp_sender_interface.h" +#include "api/scoped_refptr.h" #include "livekit/media_stream.h" #include "livekit/rtc_error.h" #include "livekit/rtp_parameters.h" @@ -34,8 +36,10 @@ namespace livekit { // TODO(theomonnom): FrameTransformer & FrameEncryptor interface class RtpSender { public: - RtpSender(std::shared_ptr rtc_runtime, - rtc::scoped_refptr sender); + RtpSender( + std::shared_ptr rtc_runtime, + rtc::scoped_refptr sender, + rtc::scoped_refptr peer_connection); bool set_track(std::shared_ptr track) const; @@ -43,6 +47,10 @@ class RtpSender { uint32_t ssrc() const; + void get_stats( + rust::Box ctx, + rust::Fn, rust::String)> on_stats) const; + MediaType media_type() const; rust::String id() const; @@ -64,6 +72,7 @@ class RtpSender { private: std::shared_ptr rtc_runtime_; rtc::scoped_refptr sender_; + rtc::scoped_refptr peer_connection_; }; static std::shared_ptr _shared_rtp_sender() { diff --git a/webrtc-sys/include/livekit/rtp_transceiver.h b/webrtc-sys/include/livekit/rtp_transceiver.h index e802245d..0eba06fb 100644 --- a/webrtc-sys/include/livekit/rtp_transceiver.h +++ b/webrtc-sys/include/livekit/rtp_transceiver.h @@ -18,9 +18,11 @@ #include +#include "api/peer_connection_interface.h" #include "api/rtp_parameters.h" #include "api/rtp_transceiver_direction.h" #include "api/rtp_transceiver_interface.h" +#include "api/scoped_refptr.h" #include "livekit/rtc_error.h" #include "livekit/rtp_parameters.h" #include "livekit/rtp_receiver.h" @@ -41,7 +43,8 @@ class RtpTransceiver { public: RtpTransceiver( std::shared_ptr rtc_runtime, - rtc::scoped_refptr transceiver); + rtc::scoped_refptr transceiver, + rtc::scoped_refptr peer_connection); MediaType media_type() const; @@ -80,6 +83,7 @@ class RtpTransceiver { private: std::shared_ptr rtc_runtime_; rtc::scoped_refptr transceiver_; + rtc::scoped_refptr peer_connection_; }; static std::shared_ptr _shared_rtp_transceiver() { diff --git a/webrtc-sys/src/helper.rs b/webrtc-sys/src/helper.rs index d1e729fd..f77de2be 100644 --- a/webrtc-sys/src/helper.rs +++ b/webrtc-sys/src/helper.rs @@ -14,7 +14,6 @@ #[cxx::bridge(namespace = "livekit")] pub mod ffi { - // Wrapper to opaque C++ objects // https://github.com/dtolnay/cxx/issues/741 // Used to allow SharedPtr/UniquePtr type inside a rust::Vec diff --git a/webrtc-sys/src/jsep.cpp b/webrtc-sys/src/jsep.cpp index 06b3f770..264c5c3c 100644 --- a/webrtc-sys/src/jsep.cpp +++ b/webrtc-sys/src/jsep.cpp @@ -111,10 +111,10 @@ std::unique_ptr create_session_description( } NativeCreateSdpObserver::NativeCreateSdpObserver( - rust::Box ctx, - rust::Fn, std::unique_ptr)> + rust::Box ctx, + rust::Fn, std::unique_ptr)> on_success, - rust::Fn, RtcError)> on_error) + rust::Fn, RtcError)> on_error) : ctx_(std::move(ctx)), on_success_(on_success), on_error_(on_error) {} void NativeCreateSdpObserver::OnSuccess( @@ -130,8 +130,8 @@ void NativeCreateSdpObserver::OnFailure(webrtc::RTCError error) { } NativeSetLocalSdpObserver::NativeSetLocalSdpObserver( - rust::Box ctx, - rust::Fn, RtcError)> on_complete) + rust::Box ctx, + rust::Fn, RtcError)> on_complete) : ctx_(std::move(ctx)), on_complete_(on_complete) {} void NativeSetLocalSdpObserver::OnSetLocalDescriptionComplete( @@ -140,8 +140,8 @@ void NativeSetLocalSdpObserver::OnSetLocalDescriptionComplete( } NativeSetRemoteSdpObserver::NativeSetRemoteSdpObserver( - rust::Box ctx, - rust::Fn, RtcError)> on_complete) + rust::Box ctx, + rust::Fn, RtcError)> on_complete) : ctx_(std::move(ctx)), on_complete_(on_complete) {} void NativeSetRemoteSdpObserver::OnSetRemoteDescriptionComplete( @@ -149,4 +149,16 @@ void NativeSetRemoteSdpObserver::OnSetRemoteDescriptionComplete( on_complete_(std::move(ctx_), to_error(error)); } +template +NativeRtcStatsCollector::NativeRtcStatsCollector( + rust::Box ctx, + rust::Fn, rust::String)> on_stats) + : ctx_(std::move(ctx)), on_stats_(on_stats) {} + +template +void NativeRtcStatsCollector::OnStatsDelivered( + const rtc::scoped_refptr& report) { + on_stats_(std::move(ctx_), report->ToJson()); +} + } // namespace livekit diff --git a/webrtc-sys/src/peer_connection.cpp b/webrtc-sys/src/peer_connection.cpp index c7bd0dd1..251b3996 100644 --- a/webrtc-sys/src/peer_connection.cpp +++ b/webrtc-sys/src/peer_connection.cpp @@ -19,6 +19,7 @@ #include #include "api/data_channel_interface.h" +#include "api/peer_connection_interface.h" #include "api/scoped_refptr.h" #include "livekit/data_channel.h" #include "livekit/jsep.h" @@ -72,12 +73,12 @@ to_native_offer_answer_options(const RtcOfferAnswerOptions& options) { } PeerConnection::PeerConnection( - std::shared_ptr rtc_runtime, - std::unique_ptr observer, - rtc::scoped_refptr peer_connection) - : rtc_runtime_(rtc_runtime), - observer_(std::move(observer)), - peer_connection_(std::move(peer_connection)) { + std::shared_ptr rtc_runtime_, + rtc::scoped_refptr pc_factory, + rust::Box observer) + : rtc_runtime_(std::move(rtc_runtime_)), + pc_factory_(std::move(pc_factory)), + observer_(std::move(observer)) { RTC_LOG(LS_VERBOSE) << "PeerConnection::PeerConnection()"; } @@ -85,6 +86,21 @@ PeerConnection::~PeerConnection() { RTC_LOG(LS_VERBOSE) << "PeerConnection::~PeerConnection()"; } +bool PeerConnection::Initialize( + webrtc::PeerConnectionInterface::RTCConfiguration config) { + webrtc::PeerConnectionDependencies deps{this}; + auto result = + pc_factory_->CreatePeerConnectionOrError(config, std::move(deps)); + + if (!result.ok()) { + RTC_LOG(LS_ERROR) << "Failed to create peer connection: " + << result.error().message(); + return false; + } + peer_connection_ = std::move(result.value()); + return true; +} + void PeerConnection::set_configuration(RtcConfiguration config) const { auto result = peer_connection_->SetConfiguration(to_native_rtc_configuration(config)); @@ -96,10 +112,10 @@ void PeerConnection::set_configuration(RtcConfiguration config) const { void PeerConnection::create_offer( RtcOfferAnswerOptions options, - rust::Box ctx, - rust::Fn, std::unique_ptr)> + rust::Box ctx, + rust::Fn, std::unique_ptr)> on_success, - rust::Fn, RtcError)> on_error) const { + rust::Fn, RtcError)> on_error) const { rtc::scoped_refptr observer = rtc::make_ref_counted(std::move(ctx), on_success, on_error); @@ -110,10 +126,10 @@ void PeerConnection::create_offer( void PeerConnection::create_answer( RtcOfferAnswerOptions options, - rust::Box ctx, - rust::Fn, std::unique_ptr)> + rust::Box ctx, + rust::Fn, std::unique_ptr)> on_success, - rust::Fn, RtcError)> on_error) const { + rust::Fn, RtcError)> on_error) const { rtc::scoped_refptr observer = rtc::make_ref_counted(std::move(ctx), on_success, on_error); @@ -124,8 +140,8 @@ void PeerConnection::create_answer( void PeerConnection::set_local_description( std::unique_ptr desc, - rust::Box ctx, - rust::Fn, RtcError)> on_complete) const { + rust::Box ctx, + rust::Fn, RtcError)> on_complete) const { rtc::scoped_refptr observer = rtc::make_ref_counted(std::move(ctx), on_complete); @@ -135,8 +151,8 @@ void PeerConnection::set_local_description( void PeerConnection::set_remote_description( std::unique_ptr desc, - rust::Box ctx, - rust::Fn, RtcError)> on_complete) const { + rust::Box ctx, + rust::Fn, RtcError)> on_complete) const { rtc::scoped_refptr observer = rtc::make_ref_counted(std::move(ctx), on_complete); @@ -150,8 +166,8 @@ void PeerConnection::restart_ice() const { void PeerConnection::add_ice_candidate( std::shared_ptr candidate, - rust::Box ctx, - rust::Fn, RtcError)> on_complete) const { + rust::Box ctx, + rust::Fn, RtcError)> on_complete) const { peer_connection_->AddIceCandidate( candidate->release(), [&](const webrtc::RTCError& err) { on_complete(std::move(ctx), to_error(err)); @@ -181,7 +197,8 @@ std::shared_ptr PeerConnection::add_track( throw std::runtime_error(serialize_error(to_error(result.error()))); } - return std::make_shared(rtc_runtime_, result.value()); + return std::make_shared(rtc_runtime_, result.value(), + peer_connection_); } void PeerConnection::remove_track(std::shared_ptr sender) const { @@ -190,6 +207,14 @@ void PeerConnection::remove_track(std::shared_ptr sender) const { throw std::runtime_error(serialize_error(to_error(error))); } +void PeerConnection::get_stats( + rust::Box ctx, + rust::Fn, rust::String)> on_stats) const { + auto observer = rtc::make_ref_counted>( + std::move(ctx), on_stats); + peer_connection_->GetStats(observer.get()); +} + std::shared_ptr PeerConnection::add_transceiver( std::shared_ptr track, RtpTransceiverInit init) const { @@ -198,7 +223,8 @@ std::shared_ptr PeerConnection::add_transceiver( if (!result.ok()) throw std::runtime_error(serialize_error(to_error(result.error()))); - return std::make_shared(rtc_runtime_, result.value()); + return std::make_shared(rtc_runtime_, result.value(), + peer_connection_); } std::shared_ptr PeerConnection::add_transceiver_for_media( @@ -211,14 +237,15 @@ std::shared_ptr PeerConnection::add_transceiver_for_media( if (!result.ok()) throw std::runtime_error(serialize_error(to_error(result.error()))); - return std::make_shared(rtc_runtime_, result.value()); + return std::make_shared(rtc_runtime_, result.value(), + peer_connection_); } rust::Vec PeerConnection::get_senders() const { rust::Vec vec; for (auto sender : peer_connection_->GetSenders()) - vec.push_back( - RtpSenderPtr{std::make_shared(rtc_runtime_, sender)}); + vec.push_back(RtpSenderPtr{ + std::make_shared(rtc_runtime_, sender, peer_connection_)}); return vec; } @@ -226,8 +253,8 @@ rust::Vec PeerConnection::get_senders() const { rust::Vec PeerConnection::get_receivers() const { rust::Vec vec; for (auto receiver : peer_connection_->GetReceivers()) - vec.push_back( - RtpReceiverPtr{std::make_shared(rtc_runtime_, receiver)}); + vec.push_back(RtpReceiverPtr{std::make_shared( + rtc_runtime_, receiver, peer_connection_)}); return vec; } @@ -235,8 +262,8 @@ rust::Vec PeerConnection::get_receivers() const { rust::Vec PeerConnection::get_transceivers() const { rust::Vec vec; for (auto transceiver : peer_connection_->GetTransceivers()) - vec.push_back(RtpTransceiverPtr{ - std::make_shared(rtc_runtime_, transceiver)}); + vec.push_back(RtpTransceiverPtr{std::make_shared( + rtc_runtime_, transceiver, peer_connection_)}); return vec; } @@ -318,70 +345,60 @@ void PeerConnection::close() const { // PeerConnectionObserver -NativePeerConnectionObserver::NativePeerConnectionObserver( - rust::Box observer) - : observer_(std::move(observer)) { - RTC_LOG(LS_INFO) << "NativePeerConnectionObserver()"; -} - -NativePeerConnectionObserver::~NativePeerConnectionObserver() { - RTC_LOG(LS_INFO) << "~NativePeerConnectionObserver()"; -} - -void NativePeerConnectionObserver::OnSignalingChange( +void PeerConnection::OnSignalingChange( webrtc::PeerConnectionInterface::SignalingState new_state) { observer_->on_signaling_change(static_cast(new_state)); } -void NativePeerConnectionObserver::OnAddStream( +void PeerConnection::OnAddStream( rtc::scoped_refptr stream) { observer_->on_add_stream(std::make_unique(rtc_runtime_, stream)); } -void NativePeerConnectionObserver::OnRemoveStream( +void PeerConnection::OnRemoveStream( rtc::scoped_refptr stream) { // Find current MediaStream // observer_->on_remove_stream(std::make_unique(rtc_runtime_, // stream)); } -void NativePeerConnectionObserver::OnDataChannel( +void PeerConnection::OnDataChannel( rtc::scoped_refptr data_channel) { observer_->on_data_channel( std::make_shared(rtc_runtime_, data_channel)); } -void NativePeerConnectionObserver::OnRenegotiationNeeded() { +void PeerConnection::OnRenegotiationNeeded() { observer_->on_renegotiation_needed(); } -void NativePeerConnectionObserver::OnNegotiationNeededEvent(uint32_t event_id) { +void PeerConnection::OnNegotiationNeededEvent(uint32_t event_id) { observer_->on_negotiation_needed_event(event_id); } -void NativePeerConnectionObserver::OnIceConnectionChange( +void PeerConnection::OnIceConnectionChange( webrtc::PeerConnectionInterface::IceConnectionState new_state) { observer_->on_ice_connection_change( static_cast(new_state)); } -void NativePeerConnectionObserver::OnStandardizedIceConnectionChange( +void PeerConnection::OnStandardizedIceConnectionChange( webrtc::PeerConnectionInterface::IceConnectionState new_state) { observer_->on_standardized_ice_connection_change( static_cast(new_state)); } -void NativePeerConnectionObserver::OnConnectionChange( +void PeerConnection::OnConnectionChange( webrtc::PeerConnectionInterface::PeerConnectionState new_state) { observer_->on_connection_change(static_cast(new_state)); } -void NativePeerConnectionObserver::OnIceGatheringChange( +void PeerConnection::OnIceGatheringChange( webrtc::PeerConnectionInterface::IceGatheringState new_state) { observer_->on_ice_gathering_change(static_cast(new_state)); } -void NativePeerConnectionObserver::OnIceCandidate( +void PeerConnection::OnIceCandidate( const webrtc::IceCandidateInterface* candidate) { auto new_candidate = webrtc::CreateIceCandidate(candidate->sdp_mid(), candidate->sdp_mline_index(), @@ -390,16 +407,15 @@ void NativePeerConnectionObserver::OnIceCandidate( std::make_unique(std::move(new_candidate))); } -void NativePeerConnectionObserver::OnIceCandidateError( - const std::string& address, - int port, - const std::string& url, - int error_code, - const std::string& error_text) { +void PeerConnection::OnIceCandidateError(const std::string& address, + int port, + const std::string& url, + int error_code, + const std::string& error_text) { observer_->on_ice_candidate_error(address, port, url, error_code, error_text); } -void NativePeerConnectionObserver::OnIceCandidatesRemoved( +void PeerConnection::OnIceCandidatesRemoved( const std::vector& candidates) { rust::Vec vec; @@ -410,12 +426,11 @@ void NativePeerConnectionObserver::OnIceCandidatesRemoved( observer_->on_ice_candidates_removed(std::move(vec)); } -void NativePeerConnectionObserver::OnIceConnectionReceivingChange( - bool receiving) { +void PeerConnection::OnIceConnectionReceivingChange(bool receiving) { observer_->on_ice_connection_receiving_change(receiving); } -void NativePeerConnectionObserver::OnIceSelectedCandidatePairChanged( +void PeerConnection::OnIceSelectedCandidatePairChanged( const cricket::CandidatePairChangeEvent& event) { CandidatePairChangeEvent e{}; e.selected_candidate_pair.local = @@ -429,7 +444,7 @@ void NativePeerConnectionObserver::OnIceSelectedCandidatePairChanged( observer_->on_ice_selected_candidate_pair_changed(std::move(e)); } -void NativePeerConnectionObserver::OnAddTrack( +void PeerConnection::OnAddTrack( rtc::scoped_refptr receiver, const std::vector>& streams) { @@ -440,30 +455,25 @@ void NativePeerConnectionObserver::OnAddTrack( MediaStreamPtr{std::make_unique(rtc_runtime_, item)}); } - observer_->on_add_track(std::make_unique(rtc_runtime_, receiver), - std::move(vec)); + observer_->on_add_track( + std::make_unique(rtc_runtime_, receiver, peer_connection_), + std::move(vec)); } -void NativePeerConnectionObserver::OnTrack( +void PeerConnection::OnTrack( rtc::scoped_refptr transceiver) { - observer_->on_track( - std::make_unique(rtc_runtime_, transceiver)); + observer_->on_track(std::make_unique( + rtc_runtime_, transceiver, peer_connection_)); } -void NativePeerConnectionObserver::OnRemoveTrack( +void PeerConnection::OnRemoveTrack( rtc::scoped_refptr receiver) { observer_->on_remove_track( - std::make_unique(rtc_runtime_, receiver)); + std::make_unique(rtc_runtime_, receiver, peer_connection_)); } -void NativePeerConnectionObserver::OnInterestingUsage(int usage_pattern) { +void PeerConnection::OnInterestingUsage(int usage_pattern) { observer_->on_interesting_usage(usage_pattern); } -std::unique_ptr -create_native_peer_connection_observer( - rust::Box observer) { - return std::make_unique(std::move(observer)); -} - } // namespace livekit diff --git a/webrtc-sys/src/peer_connection.rs b/webrtc-sys/src/peer_connection.rs index 526f9107..602a396d 100644 --- a/webrtc-sys/src/peer_connection.rs +++ b/webrtc-sys/src/peer_connection.rs @@ -12,31 +12,11 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::candidate::ffi::Candidate; -use crate::data_channel::ffi::DataChannel; use crate::impl_thread_safety; -use crate::jsep::ffi::IceCandidate; -use crate::media_stream::ffi::MediaStream; -use crate::rtp_receiver::ffi::RtpReceiver; -use crate::rtp_transceiver::ffi::RtpTransceiver; -use cxx::SharedPtr; use std::any::Any; -use std::sync::Arc; #[cxx::bridge(namespace = "livekit")] pub mod ffi { - pub struct CandidatePair { - local: SharedPtr, - remote: SharedPtr, - } - - pub struct CandidatePairChangeEvent { - selected_candidate_pair: CandidatePair, - last_data_received_ms: i64, - reason: String, - estimated_disconnected_time_ms: i64, - } - #[repr(i32)] pub enum PeerConnectionState { New, @@ -76,6 +56,20 @@ pub mod ffi { IceGatheringComplete, } + #[repr(i32)] + pub enum ContinualGatheringPolicy { + GatherOnce, + GatherContinually, + } + + #[repr(i32)] + pub enum IceTransportsType { + None, + Relay, + NoHost, + All, + } + pub struct RtcOfferAnswerOptions { offer_to_receive_video: i32, offer_to_receive_audio: i32, @@ -93,20 +87,6 @@ pub mod ffi { pub password: String, } - #[repr(i32)] - pub enum ContinualGatheringPolicy { - GatherOnce, - GatherContinually, - } - - #[repr(i32)] - pub enum IceTransportsType { - None, - Relay, - NoHost, - All, - } - pub struct RtcConfiguration { pub ice_servers: Vec, pub continual_gathering_policy: ContinualGatheringPolicy, @@ -125,8 +105,6 @@ pub mod ffi { include!("livekit/jsep.h"); include!("livekit/webrtc.h"); - type MediaStreamPtr = crate::helper::ffi::MediaStreamPtr; - type CandidatePtr = crate::helper::ffi::CandidatePtr; type RtpSenderPtr = crate::helper::ffi::RtpSenderPtr; type RtpReceiverPtr = crate::helper::ffi::RtpReceiverPtr; type RtpTransceiverPtr = crate::helper::ffi::RtpTransceiverPtr; @@ -150,42 +128,33 @@ pub mod ffi { type PeerConnection; - // The reason we still expose NativePeerConnectionObserver is because cxx doeesn't support Rust type alias - // So we can't share NativePeerConnectionWrapper in peer_connection_factory.rs - // (It is technically possible to get the Opaque C++ Type, but in this case, we can't use Box) - // We can delete create_native_peer_connection_observer once cxx supports Rust type alias - type NativePeerConnectionObserver; - fn create_native_peer_connection_observer( - observer: Box, - ) -> UniquePtr; - fn set_configuration(self: &PeerConnection, config: RtcConfiguration) -> Result<()>; fn create_offer( self: &PeerConnection, options: RtcOfferAnswerOptions, - ctx: Box, - on_success: fn(ctx: Box, sdp: UniquePtr), - on_error: fn(ctx: Box, error: RtcError), + ctx: Box, + on_success: fn(ctx: Box, sdp: UniquePtr), + on_error: fn(ctx: Box, error: RtcError), ); fn create_answer( self: &PeerConnection, options: RtcOfferAnswerOptions, - ctx: Box, - on_success: fn(ctx: Box, sdp: UniquePtr), - on_error: fn(ctx: Box, error: RtcError), + ctx: Box, + on_success: fn(ctx: Box, sdp: UniquePtr), + on_error: fn(ctx: Box, error: RtcError), ); fn set_local_description( self: &PeerConnection, desc: UniquePtr, - ctx: Box, - on_complete: fn(ctx: Box, error: RtcError), + ctx: Box, + on_complete: fn(ctx: Box, error: RtcError), ); fn set_remote_description( self: &PeerConnection, desc: UniquePtr, - ctx: Box, - on_complete: fn(ctx: Box, error: RtcError), + ctx: Box, + on_complete: fn(ctx: Box, error: RtcError), ); fn add_track( self: &PeerConnection, @@ -193,6 +162,11 @@ pub mod ffi { stream_ids: &Vec, ) -> Result>; fn remove_track(self: &PeerConnection, sender: SharedPtr) -> Result<()>; + fn get_stats( + self: &PeerConnection, + ctx: Box, + on_stats: fn(ctx: Box, json: String), + ); fn add_transceiver( self: &PeerConnection, track: SharedPtr, @@ -214,8 +188,8 @@ pub mod ffi { fn add_ice_candidate( self: &PeerConnection, candidate: SharedPtr, - ctx: Box, - on_complete: fn(ctx: Box, error: RtcError), + ctx: Box, + on_complete: fn(ctx: Box, error: RtcError), ); fn restart_ice(self: &PeerConnection); fn current_local_description(self: &PeerConnection) -> UniquePtr; @@ -230,71 +204,12 @@ pub mod ffi { } extern "Rust" { - type AsyncContext; - type PeerConnectionObserverWrapper; - - fn on_signaling_change(self: &PeerConnectionObserverWrapper, new_state: SignalingState); - fn on_add_stream(self: &PeerConnectionObserverWrapper, stream: SharedPtr); - fn on_remove_stream(self: &PeerConnectionObserverWrapper, stream: SharedPtr); - fn on_data_channel( - self: &PeerConnectionObserverWrapper, - data_channel: SharedPtr, - ); - fn on_renegotiation_needed(self: &PeerConnectionObserverWrapper); - fn on_negotiation_needed_event(self: &PeerConnectionObserverWrapper, event: u32); - fn on_ice_connection_change( - self: &PeerConnectionObserverWrapper, - new_state: IceConnectionState, - ); - fn on_standardized_ice_connection_change( - self: &PeerConnectionObserverWrapper, - new_state: IceConnectionState, - ); - fn on_connection_change( - self: &PeerConnectionObserverWrapper, - new_state: PeerConnectionState, - ); - fn on_ice_gathering_change( - self: &PeerConnectionObserverWrapper, - new_state: IceGatheringState, - ); - fn on_ice_candidate( - self: &PeerConnectionObserverWrapper, - candidate: SharedPtr, - ); - fn on_ice_candidate_error( - self: &PeerConnectionObserverWrapper, - address: String, - port: i32, - url: String, - error_code: i32, - error_text: String, - ); - fn on_ice_candidates_removed( - self: &PeerConnectionObserverWrapper, - removed: Vec, - ); - fn on_ice_connection_receiving_change( - self: &PeerConnectionObserverWrapper, - receiving: bool, - ); - fn on_ice_selected_candidate_pair_changed( - self: &PeerConnectionObserverWrapper, - event: CandidatePairChangeEvent, - ); - fn on_add_track( - self: &PeerConnectionObserverWrapper, - receiver: SharedPtr, - streams: Vec, - ); - fn on_track(self: &PeerConnectionObserverWrapper, transceiver: SharedPtr); - fn on_remove_track(self: &PeerConnectionObserverWrapper, receiver: SharedPtr); - fn on_interesting_usage(self: &PeerConnectionObserverWrapper, usage_pattern: i32); + type PeerContext; } } #[repr(transparent)] -pub struct AsyncContext(pub Box); +pub struct PeerContext(pub Box); // https://webrtc.github.io/webrtc-org/native-code/native-apis/ impl_thread_safety!(ffi::PeerConnection, Send + Sync); @@ -317,131 +232,3 @@ impl Default for ffi::RtcOfferAnswerOptions { } } } - -pub trait PeerConnectionObserver: Send + Sync { - fn on_signaling_change(&self, new_state: ffi::SignalingState); - fn on_add_stream(&self, stream: SharedPtr); - fn on_remove_stream(&self, stream: SharedPtr); - fn on_data_channel(&self, data_channel: SharedPtr); - fn on_renegotiation_needed(&self); - fn on_negotiation_needed_event(&self, event: u32); - fn on_ice_connection_change(&self, new_state: ffi::IceConnectionState); - fn on_standardized_ice_connection_change(&self, new_state: ffi::IceConnectionState); - fn on_connection_change(&self, new_state: ffi::PeerConnectionState); - fn on_ice_gathering_change(&self, new_state: ffi::IceGatheringState); - fn on_ice_candidate(&self, candidate: SharedPtr); - fn on_ice_candidate_error( - &self, - address: String, - port: i32, - url: String, - error_code: i32, - error_text: String, - ); - fn on_ice_candidates_removed(&self, removed: Vec>); - fn on_ice_connection_receiving_change(&self, receiving: bool); - fn on_ice_selected_candidate_pair_changed(&self, event: ffi::CandidatePairChangeEvent); - fn on_add_track(&self, receiver: SharedPtr, streams: Vec>); - fn on_track(&self, transceiver: SharedPtr); - fn on_remove_track(&self, receiver: SharedPtr); - fn on_interesting_usage(&self, usage_pattern: i32); -} - -// Wrapper for PeerConnectionObserver because cxx doesn't support dyn Trait on c++ -// https://github.com/dtolnay/cxx/issues/665 -pub struct PeerConnectionObserverWrapper { - observer: Arc, -} - -impl PeerConnectionObserverWrapper { - pub fn new(observer: Arc) -> Self { - Self { observer } - } - - fn on_signaling_change(&self, new_state: ffi::SignalingState) { - self.observer.on_signaling_change(new_state); - } - - fn on_add_stream(&self, stream: SharedPtr) { - self.observer.on_add_stream(stream); - } - - fn on_remove_stream(&self, stream: SharedPtr) { - self.observer.on_remove_stream(stream); - } - - fn on_data_channel(&self, data_channel: SharedPtr) { - self.observer.on_data_channel(data_channel); - } - - fn on_renegotiation_needed(&self) { - self.observer.on_renegotiation_needed(); - } - - fn on_negotiation_needed_event(&self, event: u32) { - self.observer.on_negotiation_needed_event(event); - } - - fn on_ice_connection_change(&self, new_state: ffi::IceConnectionState) { - self.observer.on_ice_connection_change(new_state); - } - - fn on_standardized_ice_connection_change(&self, new_state: ffi::IceConnectionState) { - self.observer - .on_standardized_ice_connection_change(new_state); - } - - fn on_connection_change(&self, new_state: ffi::PeerConnectionState) { - self.observer.on_connection_change(new_state); - } - - fn on_ice_gathering_change(&self, new_state: ffi::IceGatheringState) { - self.observer.on_ice_gathering_change(new_state); - } - - fn on_ice_candidate(&self, candidate: SharedPtr) { - self.observer.on_ice_candidate(candidate); - } - - fn on_ice_candidate_error( - &self, - address: String, - port: i32, - url: String, - error_code: i32, - error_text: String, - ) { - self.observer - .on_ice_candidate_error(address, port, url, error_code, error_text); - } - - fn on_ice_candidates_removed(&self, candidates: Vec) { - self.observer - .on_ice_candidates_removed(candidates.into_iter().map(|v| v.ptr).collect()); - } - - fn on_ice_connection_receiving_change(&self, receiving: bool) { - self.observer.on_ice_connection_receiving_change(receiving); - } - - fn on_ice_selected_candidate_pair_changed(&self, event: ffi::CandidatePairChangeEvent) { - self.observer.on_ice_selected_candidate_pair_changed(event); - } - - fn on_add_track(&self, receiver: SharedPtr, streams: Vec) { - self.observer - .on_add_track(receiver, streams.into_iter().map(|v| v.ptr).collect()); - } - - fn on_track(&self, transceiver: SharedPtr) { - self.observer.on_track(transceiver); - } - - fn on_remove_track(&self, receiver: SharedPtr) { - self.observer.on_remove_track(receiver); - } - - fn on_interesting_usage(&self, usage_pattern: i32) { - self.observer.on_interesting_usage(usage_pattern); - } -} diff --git a/webrtc-sys/src/peer_connection_factory.cpp b/webrtc-sys/src/peer_connection_factory.cpp index 9a4c054f..28e400f6 100644 --- a/webrtc-sys/src/peer_connection_factory.cpp +++ b/webrtc-sys/src/peer_connection_factory.cpp @@ -22,6 +22,7 @@ #include "api/audio_codecs/builtin_audio_decoder_factory.h" #include "api/audio_codecs/builtin_audio_encoder_factory.h" #include "api/peer_connection_interface.h" +#include "api/rtc_error.h" #include "api/rtc_event_log/rtc_event_log_factory.h" #include "api/task_queue/default_task_queue_factory.h" #include "api/video_codecs/builtin_video_decoder_factory.h" @@ -40,6 +41,8 @@ namespace livekit { +class PeerConnectionObserver; + PeerConnectionFactory::PeerConnectionFactory( std::shared_ptr rtc_runtime) : rtc_runtime_(rtc_runtime) { @@ -96,18 +99,16 @@ PeerConnectionFactory::~PeerConnectionFactory() { std::shared_ptr PeerConnectionFactory::create_peer_connection( RtcConfiguration config, - std::unique_ptr observer) const { - observer->rtc_runtime_ = rtc_runtime_; // See peer_connection.h - webrtc::PeerConnectionDependencies deps{observer.get()}; - auto result = peer_factory_->CreatePeerConnectionOrError( - to_native_rtc_configuration(config), std::move(deps)); - - if (!result.ok()) { - throw std::runtime_error(serialize_error(to_error(result.error()))); + rust::Box observer) const { + std::shared_ptr pc = std::make_shared( + rtc_runtime_, peer_factory_, std::move(observer)); + + if (!pc->Initialize(to_native_rtc_configuration(config))) { + throw std::runtime_error(serialize_error(to_error(webrtc::RTCError( + webrtc::RTCErrorType::INTERNAL_ERROR, "failed to initialize pc")))); } - return std::make_shared(rtc_runtime_, std::move(observer), - result.value()); + return pc; } std::shared_ptr PeerConnectionFactory::create_video_track( diff --git a/webrtc-sys/src/peer_connection_factory.rs b/webrtc-sys/src/peer_connection_factory.rs index da7ff7a0..8d9571ea 100644 --- a/webrtc-sys/src/peer_connection_factory.rs +++ b/webrtc-sys/src/peer_connection_factory.rs @@ -12,26 +12,73 @@ // See the License for the specific language governing permissions and // limitations under the License. +use crate::candidate::ffi::Candidate; +use crate::data_channel::ffi::DataChannel; use crate::impl_thread_safety; +use crate::jsep::ffi::IceCandidate; +use crate::media_stream::ffi::MediaStream; +use crate::rtp_receiver::ffi::RtpReceiver; +use crate::rtp_transceiver::ffi::RtpTransceiver; +use cxx::SharedPtr; +use std::sync::Arc; #[cxx::bridge(namespace = "livekit")] pub mod ffi { + pub struct CandidatePair { + local: SharedPtr, + remote: SharedPtr, + } + + pub struct CandidatePairChangeEvent { + selected_candidate_pair: CandidatePair, + last_data_received_ms: i64, + reason: String, + estimated_disconnected_time_ms: i64, + } extern "C++" { - include!("livekit/media_stream.h"); - include!("livekit/webrtc.h"); include!("livekit/peer_connection_factory.h"); include!("livekit/rtp_parameters.h"); + include!("livekit/rtc_error.h"); + include!("livekit/helper.h"); + include!("livekit/candidate.h"); + include!("livekit/media_stream.h"); + include!("livekit/rtp_transceiver.h"); + include!("livekit/rtp_sender.h"); + include!("livekit/rtp_receiver.h"); + include!("livekit/data_channel.h"); + include!("livekit/jsep.h"); + include!("livekit/webrtc.h"); + include!("livekit/peer_connection.h"); + type RtcConfiguration = crate::peer_connection::ffi::RtcConfiguration; + type PeerConnectionState = crate::peer_connection::ffi::PeerConnectionState; + type SignalingState = crate::peer_connection::ffi::SignalingState; + type IceConnectionState = crate::peer_connection::ffi::IceConnectionState; + type IceGatheringState = crate::peer_connection::ffi::IceGatheringState; type AudioTrackSource = crate::audio_track::ffi::AudioTrackSource; type VideoTrackSource = crate::video_track::ffi::VideoTrackSource; + type RtpCapabilities = crate::rtp_parameters::ffi::RtpCapabilities; type AudioTrack = crate::audio_track::ffi::AudioTrack; type VideoTrack = crate::video_track::ffi::VideoTrack; - type RtpCapabilities = crate::rtp_parameters::ffi::RtpCapabilities; + type MediaStreamPtr = crate::helper::ffi::MediaStreamPtr; + type CandidatePtr = crate::helper::ffi::CandidatePtr; + type RtpSenderPtr = crate::helper::ffi::RtpSenderPtr; + type RtpReceiverPtr = crate::helper::ffi::RtpReceiverPtr; + type RtpTransceiverPtr = crate::helper::ffi::RtpTransceiverPtr; + type RtcError = crate::rtc_error::ffi::RtcError; + type Candidate = crate::candidate::ffi::Candidate; + type IceCandidate = crate::jsep::ffi::IceCandidate; + type DataChannel = crate::data_channel::ffi::DataChannel; + type DataChannelInit = crate::data_channel::ffi::DataChannelInit; + type RtpSender = crate::rtp_sender::ffi::RtpSender; + type RtpReceiver = crate::rtp_receiver::ffi::RtpReceiver; + type RtpTransceiver = crate::rtp_transceiver::ffi::RtpTransceiver; + type RtpTransceiverInit = crate::rtp_transceiver::ffi::RtpTransceiverInit; + type MediaStream = crate::media_stream::ffi::MediaStream; + type MediaStreamTrack = crate::media_stream::ffi::MediaStreamTrack; + type SessionDescription = crate::jsep::ffi::SessionDescription; type MediaType = crate::webrtc::ffi::MediaType; - type NativePeerConnectionObserver = - crate::peer_connection::ffi::NativePeerConnectionObserver; - type RtcConfiguration = crate::peer_connection::ffi::RtcConfiguration; } unsafe extern "C++" { @@ -45,7 +92,7 @@ pub mod ffi { fn create_peer_connection( self: &PeerConnectionFactory, config: RtcConfiguration, - observer: UniquePtr, + observer: Box, ) -> Result>; fn create_video_track( @@ -70,6 +117,196 @@ pub mod ffi { kind: MediaType, ) -> RtpCapabilities; } + + extern "Rust" { + type PeerConnectionObserverWrapper; + + fn on_signaling_change(self: &PeerConnectionObserverWrapper, new_state: SignalingState); + fn on_add_stream(self: &PeerConnectionObserverWrapper, stream: SharedPtr); + fn on_remove_stream(self: &PeerConnectionObserverWrapper, stream: SharedPtr); + fn on_data_channel( + self: &PeerConnectionObserverWrapper, + data_channel: SharedPtr, + ); + fn on_renegotiation_needed(self: &PeerConnectionObserverWrapper); + fn on_negotiation_needed_event(self: &PeerConnectionObserverWrapper, event: u32); + fn on_ice_connection_change( + self: &PeerConnectionObserverWrapper, + new_state: IceConnectionState, + ); + fn on_standardized_ice_connection_change( + self: &PeerConnectionObserverWrapper, + new_state: IceConnectionState, + ); + fn on_connection_change( + self: &PeerConnectionObserverWrapper, + new_state: PeerConnectionState, + ); + fn on_ice_gathering_change( + self: &PeerConnectionObserverWrapper, + new_state: IceGatheringState, + ); + fn on_ice_candidate( + self: &PeerConnectionObserverWrapper, + candidate: SharedPtr, + ); + fn on_ice_candidate_error( + self: &PeerConnectionObserverWrapper, + address: String, + port: i32, + url: String, + error_code: i32, + error_text: String, + ); + fn on_ice_candidates_removed( + self: &PeerConnectionObserverWrapper, + removed: Vec, + ); + fn on_ice_connection_receiving_change( + self: &PeerConnectionObserverWrapper, + receiving: bool, + ); + fn on_ice_selected_candidate_pair_changed( + self: &PeerConnectionObserverWrapper, + event: CandidatePairChangeEvent, + ); + fn on_add_track( + self: &PeerConnectionObserverWrapper, + receiver: SharedPtr, + streams: Vec, + ); + fn on_track(self: &PeerConnectionObserverWrapper, transceiver: SharedPtr); + fn on_remove_track(self: &PeerConnectionObserverWrapper, receiver: SharedPtr); + fn on_interesting_usage(self: &PeerConnectionObserverWrapper, usage_pattern: i32); + } } impl_thread_safety!(ffi::PeerConnectionFactory, Send + Sync); + +pub trait PeerConnectionObserver: Send + Sync { + fn on_signaling_change(&self, new_state: ffi::SignalingState); + fn on_add_stream(&self, stream: SharedPtr); + fn on_remove_stream(&self, stream: SharedPtr); + fn on_data_channel(&self, data_channel: SharedPtr); + fn on_renegotiation_needed(&self); + fn on_negotiation_needed_event(&self, event: u32); + fn on_ice_connection_change(&self, new_state: ffi::IceConnectionState); + fn on_standardized_ice_connection_change(&self, new_state: ffi::IceConnectionState); + fn on_connection_change(&self, new_state: ffi::PeerConnectionState); + fn on_ice_gathering_change(&self, new_state: ffi::IceGatheringState); + fn on_ice_candidate(&self, candidate: SharedPtr); + fn on_ice_candidate_error( + &self, + address: String, + port: i32, + url: String, + error_code: i32, + error_text: String, + ); + fn on_ice_candidates_removed(&self, removed: Vec>); + fn on_ice_connection_receiving_change(&self, receiving: bool); + fn on_ice_selected_candidate_pair_changed(&self, event: ffi::CandidatePairChangeEvent); + fn on_add_track(&self, receiver: SharedPtr, streams: Vec>); + fn on_track(&self, transceiver: SharedPtr); + fn on_remove_track(&self, receiver: SharedPtr); + fn on_interesting_usage(&self, usage_pattern: i32); +} + +// Wrapper for PeerConnectionObserver because cxx doesn't support dyn Trait on c++ +// https://github.com/dtolnay/cxx/issues/665 +pub struct PeerConnectionObserverWrapper { + observer: Arc, +} + +impl PeerConnectionObserverWrapper { + pub fn new(observer: Arc) -> Self { + Self { observer } + } + + fn on_signaling_change(&self, new_state: ffi::SignalingState) { + self.observer.on_signaling_change(new_state); + } + + fn on_add_stream(&self, stream: SharedPtr) { + self.observer.on_add_stream(stream); + } + + fn on_remove_stream(&self, stream: SharedPtr) { + self.observer.on_remove_stream(stream); + } + + fn on_data_channel(&self, data_channel: SharedPtr) { + self.observer.on_data_channel(data_channel); + } + + fn on_renegotiation_needed(&self) { + self.observer.on_renegotiation_needed(); + } + + fn on_negotiation_needed_event(&self, event: u32) { + self.observer.on_negotiation_needed_event(event); + } + + fn on_ice_connection_change(&self, new_state: ffi::IceConnectionState) { + self.observer.on_ice_connection_change(new_state); + } + + fn on_standardized_ice_connection_change(&self, new_state: ffi::IceConnectionState) { + self.observer + .on_standardized_ice_connection_change(new_state); + } + + fn on_connection_change(&self, new_state: ffi::PeerConnectionState) { + self.observer.on_connection_change(new_state); + } + + fn on_ice_gathering_change(&self, new_state: ffi::IceGatheringState) { + self.observer.on_ice_gathering_change(new_state); + } + + fn on_ice_candidate(&self, candidate: SharedPtr) { + self.observer.on_ice_candidate(candidate); + } + + fn on_ice_candidate_error( + &self, + address: String, + port: i32, + url: String, + error_code: i32, + error_text: String, + ) { + self.observer + .on_ice_candidate_error(address, port, url, error_code, error_text); + } + + fn on_ice_candidates_removed(&self, candidates: Vec) { + self.observer + .on_ice_candidates_removed(candidates.into_iter().map(|v| v.ptr).collect()); + } + + fn on_ice_connection_receiving_change(&self, receiving: bool) { + self.observer.on_ice_connection_receiving_change(receiving); + } + + fn on_ice_selected_candidate_pair_changed(&self, event: ffi::CandidatePairChangeEvent) { + self.observer.on_ice_selected_candidate_pair_changed(event); + } + + fn on_add_track(&self, receiver: SharedPtr, streams: Vec) { + self.observer + .on_add_track(receiver, streams.into_iter().map(|v| v.ptr).collect()); + } + + fn on_track(&self, transceiver: SharedPtr) { + self.observer.on_track(transceiver); + } + + fn on_remove_track(&self, receiver: SharedPtr) { + self.observer.on_remove_track(receiver); + } + + fn on_interesting_usage(&self, usage_pattern: i32) { + self.observer.on_interesting_usage(usage_pattern); + } +} diff --git a/webrtc-sys/src/rtp_receiver.cpp b/webrtc-sys/src/rtp_receiver.cpp index 14754e85..857f7a71 100644 --- a/webrtc-sys/src/rtp_receiver.cpp +++ b/webrtc-sys/src/rtp_receiver.cpp @@ -15,17 +15,23 @@ */ #include "livekit/rtp_receiver.h" +#include "livekit/jsep.h" #include #include "absl/types/optional.h" +#include "api/peer_connection_interface.h" +#include "api/scoped_refptr.h" namespace livekit { RtpReceiver::RtpReceiver( std::shared_ptr rtc_runtime, - rtc::scoped_refptr receiver) - : rtc_runtime_(rtc_runtime), receiver_(std::move(receiver)) {} + rtc::scoped_refptr receiver, + rtc::scoped_refptr peer_connection) + : rtc_runtime_(rtc_runtime), + receiver_(std::move(receiver)), + peer_connection_(std::move(peer_connection)) {} std::shared_ptr RtpReceiver::track() const { return rtc_runtime_->get_or_create_media_stream_track(receiver_->track()); @@ -38,6 +44,14 @@ rust::Vec RtpReceiver::stream_ids() const { return rust; } +void RtpReceiver::get_stats( + rust::Box ctx, + rust::Fn, rust::String)> on_stats) const { + auto observer = + rtc::make_ref_counted>(std::move(ctx), on_stats); + peer_connection_->GetStats(receiver_, observer); +} + rust::Vec RtpReceiver::streams() const { rust::Vec rust; for (auto stream : receiver_->streams()) diff --git a/webrtc-sys/src/rtp_receiver.rs b/webrtc-sys/src/rtp_receiver.rs index e7f3caff..58e2725a 100644 --- a/webrtc-sys/src/rtp_receiver.rs +++ b/webrtc-sys/src/rtp_receiver.rs @@ -13,6 +13,7 @@ // limitations under the License. use crate::impl_thread_safety; +use std::any::Any; #[cxx::bridge(namespace = "livekit")] pub mod ffi { @@ -36,6 +37,11 @@ pub mod ffi { type RtpReceiver; fn track(self: &RtpReceiver) -> SharedPtr; + fn get_stats( + self: &RtpReceiver, + ctx: Box, + on_stats: fn(ctx: Box, json: String), + ); fn stream_ids(self: &RtpReceiver) -> Vec; fn streams(self: &RtpReceiver) -> Vec; fn media_type(self: &RtpReceiver) -> MediaType; @@ -45,6 +51,12 @@ pub mod ffi { fn _shared_rtp_receiver() -> SharedPtr; } + + extern "Rust" { + type ReceiverContext; + } } +pub struct ReceiverContext(pub Box); + impl_thread_safety!(ffi::RtpReceiver, Send + Sync); diff --git a/webrtc-sys/src/rtp_sender.cpp b/webrtc-sys/src/rtp_sender.cpp index 3d618767..8d5008f8 100644 --- a/webrtc-sys/src/rtp_sender.cpp +++ b/webrtc-sys/src/rtp_sender.cpp @@ -15,12 +15,22 @@ */ #include "livekit/rtp_sender.h" +#include "livekit/jsep.h" + +#include "rust/cxx.h" +#include "webrtc-sys/src/rtp_sender.rs.h" namespace livekit { -RtpSender::RtpSender(std::shared_ptr rtc_runtime, - rtc::scoped_refptr sender) - : rtc_runtime_(rtc_runtime), sender_(std::move(sender)) {} + + +RtpSender::RtpSender( + std::shared_ptr rtc_runtime, + rtc::scoped_refptr sender, + rtc::scoped_refptr peer_connection) + : rtc_runtime_(rtc_runtime), + sender_(std::move(sender)), + peer_connection_(std::move(peer_connection)) {} bool RtpSender::set_track(std::shared_ptr track) const { return sender_->SetTrack(track->rtc_track().get()); @@ -34,6 +44,14 @@ uint32_t RtpSender::ssrc() const { return sender_->ssrc(); } +void RtpSender::get_stats( + rust::Box ctx, + rust::Fn, rust::String)> on_stats) const { + auto observer = + rtc::make_ref_counted>(std::move(ctx), on_stats); + peer_connection_->GetStats(sender_, observer); +} + MediaType RtpSender::media_type() const { return static_cast(sender_->media_type()); } diff --git a/webrtc-sys/src/rtp_sender.rs b/webrtc-sys/src/rtp_sender.rs index 575bcdaa..42a274a7 100644 --- a/webrtc-sys/src/rtp_sender.rs +++ b/webrtc-sys/src/rtp_sender.rs @@ -13,6 +13,7 @@ // limitations under the License. use crate::impl_thread_safety; +use std::any::Any; #[cxx::bridge(namespace = "livekit")] pub mod ffi { @@ -35,6 +36,11 @@ pub mod ffi { fn set_track(self: &RtpSender, track: SharedPtr) -> bool; fn track(self: &RtpSender) -> SharedPtr; + fn get_stats( + self: &RtpSender, + ctx: Box, + on_stats: fn(ctx: Box, json: String), + ); fn ssrc(self: &RtpSender) -> u32; fn media_type(self: &RtpSender) -> MediaType; fn id(self: &RtpSender) -> String; @@ -46,6 +52,13 @@ pub mod ffi { fn _shared_rtp_sender() -> SharedPtr; } + + extern "Rust" { + type SenderContext; + } } +#[repr(transparent)] +pub struct SenderContext(pub Box); + impl_thread_safety!(ffi::RtpSender, Send + Sync); diff --git a/webrtc-sys/src/rtp_transceiver.cpp b/webrtc-sys/src/rtp_transceiver.cpp index 3deaff89..82eb73b2 100644 --- a/webrtc-sys/src/rtp_transceiver.cpp +++ b/webrtc-sys/src/rtp_transceiver.cpp @@ -16,6 +16,9 @@ #include "livekit/rtp_transceiver.h" +#include "api/peer_connection_interface.h" +#include "api/scoped_refptr.h" + namespace livekit { webrtc::RtpTransceiverInit to_native_rtp_transceiver_init( @@ -35,8 +38,11 @@ webrtc::RtpTransceiverInit to_native_rtp_transceiver_init( RtpTransceiver::RtpTransceiver( std::shared_ptr rtc_runtime, - rtc::scoped_refptr transceiver) - : rtc_runtime_(rtc_runtime), transceiver_(std::move(transceiver)) {} + rtc::scoped_refptr transceiver, + rtc::scoped_refptr peer_connection) + : rtc_runtime_(rtc_runtime), + transceiver_(std::move(transceiver)), + peer_connection_(std::move(peer_connection)) {} MediaType RtpTransceiver::media_type() const { return static_cast(transceiver_->media_type()); @@ -49,11 +55,13 @@ rust::String RtpTransceiver::mid() const { } std::shared_ptr RtpTransceiver::sender() const { - return std::make_shared(rtc_runtime_, transceiver_->sender()); + return std::make_shared(rtc_runtime_, transceiver_->sender(), + peer_connection_); } std::shared_ptr RtpTransceiver::receiver() const { - return std::make_shared(rtc_runtime_, transceiver_->receiver()); + return std::make_shared(rtc_runtime_, transceiver_->receiver(), + peer_connection_); } bool RtpTransceiver::stopped() const {