1 use crate::data_channel::data_channel_state::RTCDataChannelState; 2 use crate::data_channel::RTCDataChannel; 3 use crate::dtls_transport::dtls_fingerprint::RTCDtlsFingerprint; 4 use crate::peer_connection::certificate::RTCCertificate; 5 use crate::rtp_transceiver::rtp_codec::RTCRtpCodecParameters; 6 use crate::rtp_transceiver::{PayloadType, SSRC}; 7 use crate::sctp_transport::RTCSctpTransport; 8 9 use ice::agent::agent_stats::{CandidatePairStats, CandidateStats}; 10 use ice::agent::Agent; 11 use ice::candidate::{CandidatePairState, CandidateType}; 12 use ice::network_type::NetworkType; 13 use stats_collector::StatsCollector; 14 15 use serde::{Serialize, Serializer}; 16 use std::collections::HashMap; 17 use std::sync::Arc; 18 use std::time::SystemTime; 19 use tokio::time::Instant; 20 21 mod serialize; 22 pub mod stats_collector; 23 24 #[derive(Debug, Serialize)] 25 pub enum RTCStatsType { 26 #[serde(rename = "candidate-pair")] 27 CandidatePair, 28 #[serde(rename = "certificate")] 29 Certificate, 30 #[serde(rename = "codec")] 31 Codec, 32 #[serde(rename = "csrc")] 33 CSRC, 34 #[serde(rename = "data-channel")] 35 DataChannel, 36 #[serde(rename = "inbound-rtp")] 37 InboundRTP, 38 #[serde(rename = "local-candidate")] 39 LocalCandidate, 40 #[serde(rename = "outbound-rtp")] 41 OutboundRTP, 42 #[serde(rename = "peer-connection")] 43 PeerConnection, 44 #[serde(rename = "receiver")] 45 Receiver, 46 #[serde(rename = "remote-candidate")] 47 RemoteCandidate, 48 #[serde(rename = "remote-inbound-rtp")] 49 RemoteInboundRTP, 50 #[serde(rename = "remote-outbound-rtp")] 51 RemoteOutboundRTP, 52 #[serde(rename = "sender")] 53 Sender, 54 #[serde(rename = "transport")] 55 Transport, 56 } 57 58 pub enum SourceStatsType { 59 LocalCandidate(CandidateStats), 60 RemoteCandidate(CandidateStats), 61 } 62 63 #[derive(Debug)] 64 pub enum StatsReportType { 65 CandidatePair(ICECandidatePairStats), 66 CertificateStats(CertificateStats), 67 Codec(CodecStats), 68 DataChannel(DataChannelStats), 69 LocalCandidate(ICECandidateStats), 70 PeerConnection(PeerConnectionStats), 71 RemoteCandidate(ICECandidateStats), 72 SCTPTransport(ICETransportStats), 73 Transport(ICETransportStats), 74 InboundRTP(InboundRTPStats), 75 OutboundRTP(OutboundRTPStats), 76 RemoteInboundRTP(RemoteInboundRTPStats), 77 RemoteOutboundRTP(RemoteOutboundRTPStats), 78 } 79 80 impl From<SourceStatsType> for StatsReportType { from(stats: SourceStatsType) -> Self81 fn from(stats: SourceStatsType) -> Self { 82 match stats { 83 SourceStatsType::LocalCandidate(stats) => StatsReportType::LocalCandidate( 84 ICECandidateStats::new(stats, RTCStatsType::LocalCandidate), 85 ), 86 SourceStatsType::RemoteCandidate(stats) => StatsReportType::RemoteCandidate( 87 ICECandidateStats::new(stats, RTCStatsType::RemoteCandidate), 88 ), 89 } 90 } 91 } 92 93 impl Serialize for StatsReportType { serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: Serializer,94 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> 95 where 96 S: Serializer, 97 { 98 match self { 99 StatsReportType::CandidatePair(stats) => stats.serialize(serializer), 100 StatsReportType::CertificateStats(stats) => stats.serialize(serializer), 101 StatsReportType::Codec(stats) => stats.serialize(serializer), 102 StatsReportType::DataChannel(stats) => stats.serialize(serializer), 103 StatsReportType::LocalCandidate(stats) => stats.serialize(serializer), 104 StatsReportType::PeerConnection(stats) => stats.serialize(serializer), 105 StatsReportType::RemoteCandidate(stats) => stats.serialize(serializer), 106 StatsReportType::SCTPTransport(stats) => stats.serialize(serializer), 107 StatsReportType::Transport(stats) => stats.serialize(serializer), 108 StatsReportType::InboundRTP(stats) => stats.serialize(serializer), 109 StatsReportType::OutboundRTP(stats) => stats.serialize(serializer), 110 StatsReportType::RemoteInboundRTP(stats) => stats.serialize(serializer), 111 StatsReportType::RemoteOutboundRTP(stats) => stats.serialize(serializer), 112 } 113 } 114 } 115 116 #[derive(Debug)] 117 pub struct StatsReport { 118 pub reports: HashMap<String, StatsReportType>, 119 } 120 121 impl From<StatsCollector> for StatsReport { from(collector: StatsCollector) -> Self122 fn from(collector: StatsCollector) -> Self { 123 StatsReport { 124 reports: collector.into_reports(), 125 } 126 } 127 } 128 129 impl Serialize for StatsReport { serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: Serializer,130 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> 131 where 132 S: Serializer, 133 { 134 self.reports.serialize(serializer) 135 } 136 } 137 138 #[derive(Debug, Serialize)] 139 #[serde(rename_all = "camelCase")] 140 pub struct ICECandidatePairStats { 141 // RTCStats 142 #[serde(with = "serialize::instant_to_epoch_seconds")] 143 pub timestamp: Instant, 144 #[serde(rename = "type")] 145 pub stats_type: RTCStatsType, 146 pub id: String, 147 148 // RTCIceCandidatePairStats 149 // TODO: Add `transportId` 150 pub local_candidate_id: String, 151 pub remote_candidate_id: String, 152 pub state: CandidatePairState, 153 pub nominated: bool, 154 pub packets_sent: u32, 155 pub packets_received: u32, 156 pub bytes_sent: u64, 157 pub bytes_received: u64, 158 #[serde(with = "serialize::instant_to_epoch_seconds")] 159 pub last_packet_sent_timestamp: Instant, 160 #[serde(with = "serialize::instant_to_epoch_seconds")] 161 pub last_packet_received_timestamp: Instant, 162 pub total_round_trip_time: f64, 163 pub current_round_trip_time: f64, 164 pub available_outgoing_bitrate: f64, 165 pub available_incoming_bitrate: f64, 166 pub requests_received: u64, 167 pub requests_sent: u64, 168 pub responses_received: u64, 169 pub responses_sent: u64, 170 pub consent_requests_sent: u64, 171 // TODO: Add `packetsDiscardedOnSend` 172 // TODO: Add `bytesDiscardedOnSend` 173 174 // Non-canon 175 pub circuit_breaker_trigger_count: u32, 176 #[serde(with = "serialize::instant_to_epoch_seconds")] 177 pub consent_expired_timestamp: Instant, 178 #[serde(with = "serialize::instant_to_epoch_seconds")] 179 pub first_request_timestamp: Instant, 180 #[serde(with = "serialize::instant_to_epoch_seconds")] 181 pub last_request_timestamp: Instant, 182 pub retransmissions_sent: u64, 183 } 184 185 impl From<CandidatePairStats> for ICECandidatePairStats { from(stats: CandidatePairStats) -> Self186 fn from(stats: CandidatePairStats) -> Self { 187 ICECandidatePairStats { 188 available_incoming_bitrate: stats.available_incoming_bitrate, 189 available_outgoing_bitrate: stats.available_outgoing_bitrate, 190 bytes_received: stats.bytes_received, 191 bytes_sent: stats.bytes_sent, 192 circuit_breaker_trigger_count: stats.circuit_breaker_trigger_count, 193 consent_expired_timestamp: stats.consent_expired_timestamp, 194 consent_requests_sent: stats.consent_requests_sent, 195 current_round_trip_time: stats.current_round_trip_time, 196 first_request_timestamp: stats.first_request_timestamp, 197 id: format!("{}-{}", stats.local_candidate_id, stats.remote_candidate_id), 198 last_packet_received_timestamp: stats.last_packet_received_timestamp, 199 last_packet_sent_timestamp: stats.last_packet_sent_timestamp, 200 last_request_timestamp: stats.last_request_timestamp, 201 local_candidate_id: stats.local_candidate_id, 202 nominated: stats.nominated, 203 packets_received: stats.packets_received, 204 packets_sent: stats.packets_sent, 205 remote_candidate_id: stats.remote_candidate_id, 206 requests_received: stats.requests_received, 207 requests_sent: stats.requests_sent, 208 responses_received: stats.responses_received, 209 responses_sent: stats.responses_sent, 210 retransmissions_sent: stats.retransmissions_sent, 211 state: stats.state, 212 stats_type: RTCStatsType::CandidatePair, 213 timestamp: stats.timestamp, 214 total_round_trip_time: stats.total_round_trip_time, 215 } 216 } 217 } 218 219 #[derive(Debug, Serialize)] 220 #[serde(rename_all = "camelCase")] 221 pub struct ICECandidateStats { 222 // RTCStats 223 #[serde(with = "serialize::instant_to_epoch_seconds")] 224 pub timestamp: Instant, 225 #[serde(rename = "type")] 226 pub stats_type: RTCStatsType, 227 pub id: String, 228 229 // RTCIceCandidateStats 230 pub candidate_type: CandidateType, 231 pub deleted: bool, 232 pub ip: String, 233 pub network_type: NetworkType, 234 pub port: u16, 235 pub priority: u32, 236 pub relay_protocol: String, 237 pub url: String, 238 } 239 240 impl ICECandidateStats { new(stats: CandidateStats, stats_type: RTCStatsType) -> Self241 fn new(stats: CandidateStats, stats_type: RTCStatsType) -> Self { 242 ICECandidateStats { 243 candidate_type: stats.candidate_type, 244 deleted: stats.deleted, 245 id: stats.id, 246 ip: stats.ip, 247 network_type: stats.network_type, 248 port: stats.port, 249 priority: stats.priority, 250 relay_protocol: stats.relay_protocol, 251 stats_type, 252 timestamp: stats.timestamp, 253 url: stats.url, 254 } 255 } 256 } 257 258 #[derive(Debug, Serialize)] 259 #[serde(rename_all = "camelCase")] 260 pub struct ICETransportStats { 261 // RTCStats 262 #[serde(with = "serialize::instant_to_epoch_seconds")] 263 pub timestamp: Instant, 264 #[serde(rename = "type")] 265 pub stats_type: RTCStatsType, 266 pub id: String, 267 268 // Non-canon 269 pub bytes_received: usize, 270 pub bytes_sent: usize, 271 } 272 273 impl ICETransportStats { new(id: String, agent: Arc<Agent>) -> Self274 pub(crate) fn new(id: String, agent: Arc<Agent>) -> Self { 275 ICETransportStats { 276 id, 277 bytes_received: agent.get_bytes_received(), 278 bytes_sent: agent.get_bytes_sent(), 279 stats_type: RTCStatsType::Transport, 280 timestamp: Instant::now(), 281 } 282 } 283 } 284 285 #[derive(Debug, Serialize)] 286 #[serde(rename_all = "camelCase")] 287 pub struct CertificateStats { 288 // RTCStats 289 #[serde(with = "serialize::instant_to_epoch_seconds")] 290 pub timestamp: Instant, 291 #[serde(rename = "type")] 292 pub stats_type: RTCStatsType, 293 pub id: String, 294 295 // RTCCertificateStats 296 pub fingerprint: String, 297 pub fingerprint_algorithm: String, 298 // TODO: Add `base64Certificate` and `issuerCertificateId`. 299 } 300 301 impl CertificateStats { new(cert: &RTCCertificate, fingerprint: RTCDtlsFingerprint) -> Self302 pub(crate) fn new(cert: &RTCCertificate, fingerprint: RTCDtlsFingerprint) -> Self { 303 CertificateStats { 304 // TODO: base64_certificate 305 fingerprint: fingerprint.value, 306 fingerprint_algorithm: fingerprint.algorithm, 307 id: cert.stats_id.clone(), 308 // TODO: issuer_certificate_id 309 stats_type: RTCStatsType::Certificate, 310 timestamp: Instant::now(), 311 } 312 } 313 } 314 315 #[derive(Debug, Serialize)] 316 #[serde(rename_all = "camelCase")] 317 pub struct CodecStats { 318 // RTCStats 319 #[serde(with = "serialize::instant_to_epoch_seconds")] 320 pub timestamp: Instant, 321 #[serde(rename = "type")] 322 pub stats_type: RTCStatsType, 323 pub id: String, 324 325 // RTCCodecStats 326 pub payload_type: PayloadType, 327 pub mime_type: String, 328 pub channels: u16, 329 pub clock_rate: u32, 330 pub sdp_fmtp_line: String, 331 // TODO: Add `transportId` 332 } 333 334 impl From<&RTCRtpCodecParameters> for CodecStats { from(codec: &RTCRtpCodecParameters) -> Self335 fn from(codec: &RTCRtpCodecParameters) -> Self { 336 CodecStats { 337 channels: codec.capability.channels, 338 clock_rate: codec.capability.clock_rate, 339 id: codec.stats_id.clone(), 340 mime_type: codec.capability.mime_type.clone(), 341 payload_type: codec.payload_type, 342 sdp_fmtp_line: codec.capability.sdp_fmtp_line.clone(), 343 stats_type: RTCStatsType::Codec, 344 timestamp: Instant::now(), 345 } 346 } 347 } 348 349 #[derive(Debug, Serialize)] 350 #[serde(rename_all = "camelCase")] 351 pub struct DataChannelStats { 352 // RTCStats 353 #[serde(with = "serialize::instant_to_epoch_seconds")] 354 pub timestamp: Instant, 355 #[serde(rename = "type")] 356 pub stats_type: RTCStatsType, 357 pub id: String, 358 359 // RTCDataChannelStats 360 pub bytes_received: usize, 361 pub bytes_sent: usize, 362 pub data_channel_identifier: u16, 363 pub label: String, 364 pub messages_received: usize, 365 pub messages_sent: usize, 366 pub protocol: String, 367 pub state: RTCDataChannelState, 368 } 369 370 impl DataChannelStats { from(data_channel: &RTCDataChannel) -> Self371 pub(crate) async fn from(data_channel: &RTCDataChannel) -> Self { 372 let state = data_channel.ready_state(); 373 374 let mut bytes_received = 0; 375 let mut bytes_sent = 0; 376 let mut messages_received = 0; 377 let mut messages_sent = 0; 378 379 let lock = data_channel.data_channel.lock().await; 380 381 if let Some(internal) = &*lock { 382 bytes_received = internal.bytes_received(); 383 bytes_sent = internal.bytes_sent(); 384 messages_received = internal.messages_received(); 385 messages_sent = internal.messages_sent(); 386 } 387 388 Self { 389 bytes_received, 390 bytes_sent, 391 data_channel_identifier: data_channel.id(), // TODO: "The value is initially null" 392 id: data_channel.stats_id.clone(), 393 label: data_channel.label.clone(), 394 messages_received, 395 messages_sent, 396 protocol: data_channel.protocol.clone(), 397 state, 398 stats_type: RTCStatsType::DataChannel, 399 timestamp: Instant::now(), 400 } 401 } 402 } 403 404 #[derive(Debug, Serialize)] 405 #[serde(rename_all = "camelCase")] 406 pub struct PeerConnectionStats { 407 // RTCStats 408 #[serde(with = "serialize::instant_to_epoch_seconds")] 409 pub timestamp: Instant, 410 #[serde(rename = "type")] 411 pub stats_type: RTCStatsType, 412 pub id: String, 413 414 // RTCPeerConnectionStats 415 pub data_channels_closed: u32, 416 pub data_channels_opened: u32, 417 418 // Non-canon 419 pub data_channels_accepted: u32, 420 pub data_channels_requested: u32, 421 } 422 423 impl PeerConnectionStats { new(transport: &RTCSctpTransport, stats_id: String, data_channels_closed: u32) -> Self424 pub fn new(transport: &RTCSctpTransport, stats_id: String, data_channels_closed: u32) -> Self { 425 PeerConnectionStats { 426 data_channels_accepted: transport.data_channels_accepted(), 427 data_channels_closed, 428 data_channels_opened: transport.data_channels_opened(), 429 data_channels_requested: transport.data_channels_requested(), 430 id: stats_id, 431 stats_type: RTCStatsType::PeerConnection, 432 timestamp: Instant::now(), 433 } 434 } 435 } 436 437 #[derive(Debug, Serialize)] 438 #[serde(rename_all = "camelCase")] 439 pub struct InboundRTPStats { 440 // RTCStats 441 #[serde(with = "serialize::instant_to_epoch_seconds")] 442 pub timestamp: Instant, 443 #[serde(rename = "type")] 444 pub stats_type: RTCStatsType, 445 pub id: String, 446 447 // RTCRtpStreamStats 448 pub ssrc: SSRC, 449 pub kind: &'static str, // Either "video" or "audio" 450 // TODO: Add tranportId 451 // TODO: Add codecId 452 453 // RTCReceivedRtpStreamStats 454 pub packets_received: u64, 455 // TODO: packetsLost 456 // TOOD: jitter(maybe, might be uattainable for the same reason as `framesDropped`) 457 // NB: `framesDropped` can't be produced since we aren't decoding, might be worth introducing a 458 // way for consumers to control this in the future. 459 460 // RTCInboundRtpStreamStats 461 pub track_identifier: String, 462 pub mid: String, 463 // TODO: `remoteId` 464 // NB: `framesDecoded`, `frameWidth`, frameHeight`, `framesPerSecond`, `qpSum`, 465 // `totalDecodeTime`, `totalInterFrameDelay`, and `totalSquaredInterFrameDelay` are all decoder 466 // specific values and can't be produced since we aren't decoding. 467 pub last_packet_received_timestamp: Option<SystemTime>, 468 pub header_bytes_received: u64, 469 // TODO: `packetsDiscarded`. This value only makes sense if we have jitter buffer, which we 470 // cannot assume. 471 // TODO: `fecPacketsReceived`, `fecPacketsDiscarded` 472 pub bytes_received: u64, 473 pub nack_count: u64, 474 pub fir_count: Option<u64>, 475 pub pli_count: Option<u64>, 476 // NB: `totalProcessingDelay`, `estimatedPlayoutTimestamp`, `jitterBufferDelay`, 477 // `jitterBufferTargetDelay`, `jitterBufferEmittedCount`, `jitterBufferMinimumDelay`, 478 // `totalSamplesReceived`, `concealedSamples`, `silentConcealedSamples`, `concealmentEvents`, 479 // `insertedSamplesForDeceleration`, `removedSamplesForAcceleration`, `audioLevel`, 480 // `totalAudioEneregy`, `totalSampleDuration`, `framesReceived, and `decoderImplementation` are 481 // all decoder specific and can't be produced since we aren't decoding. 482 } 483 484 #[derive(Debug, Serialize)] 485 #[serde(rename_all = "camelCase")] 486 pub struct OutboundRTPStats { 487 // RTCStats 488 #[serde(with = "serialize::instant_to_epoch_seconds")] 489 pub timestamp: Instant, 490 #[serde(rename = "type")] 491 pub stats_type: RTCStatsType, 492 pub id: String, 493 494 // RTCRtpStreamStats 495 pub ssrc: SSRC, 496 pub kind: &'static str, // Either "video" or "audio" 497 // TODO: Add tranportId 498 // TODO: Add codecId 499 500 // RTCSentRtpStreamStats 501 pub packets_sent: u64, 502 pub bytes_sent: u64, 503 504 // RTCOutboundRtpStreamStats 505 // NB: non-canon in browsers this is available via `RTCMediaSourceStats` which we are unlikely to implement 506 pub track_identifier: String, 507 pub mid: String, 508 // TODO: `mediaSourceId` and `remoteId` 509 pub rid: Option<String>, 510 pub header_bytes_sent: u64, 511 // TODO: `retransmittedPacketsSent` and `retransmittedPacketsSent` 512 // NB: `targetBitrate`, `totalEncodedBytesTarget`, `frameWidth` `frameHeight`, `framesPerSecond`, `framesSent`, 513 // `hugeFramesSent`, `framesEncoded`, `keyFramesEncoded`, `qpSum`, and `totalEncodeTime` are 514 // all encoder specific and can't be produced snce we aren't encoding. 515 // TODO: `totalPacketSendDelay` time from `TrackLocalWriter::write_rtp` to being written to 516 // socket. 517 518 // NB: `qualityLimitationReason`, `qualityLimitationDurations`, and `qualityLimitationResolutionChanges` are all 519 // encoder specific and can't be produced since we aren't encoding. 520 pub nack_count: u64, 521 pub fir_count: Option<u64>, 522 pub pli_count: Option<u64>, 523 // NB: `encoderImplementation` is encoder specific and can't be produced since we aren't 524 // encoding. 525 } 526 527 #[derive(Debug, Serialize)] 528 #[serde(rename_all = "camelCase")] 529 pub struct RemoteInboundRTPStats { 530 // RTCStats 531 #[serde(with = "serialize::instant_to_epoch_seconds")] 532 pub timestamp: Instant, 533 #[serde(rename = "type")] 534 pub stats_type: RTCStatsType, 535 pub id: String, 536 537 // RTCRtpStreamStats 538 pub ssrc: SSRC, 539 pub kind: &'static str, // Either "video" or "audio" 540 // TODO: Add tranportId 541 // TODO: Add codecId 542 543 // RTCReceivedRtpStreamStats 544 pub packets_received: u64, 545 pub packets_lost: i64, 546 // TOOD: jitter(maybe, might be uattainable for the same reason as `framesDropped`) 547 // NB: `framesDropped` can't be produced since we aren't decoding, might be worth introducing a 548 // way for consumers to control this in the future. 549 550 // RTCRemoteInboundRtpStreamStats 551 pub local_id: String, 552 pub round_trip_time: Option<f64>, 553 pub total_round_trip_time: f64, 554 pub fraction_lost: f64, 555 pub round_trip_time_measurements: u64, 556 } 557 558 #[derive(Debug, Serialize)] 559 #[serde(rename_all = "camelCase")] 560 pub struct RemoteOutboundRTPStats { 561 // RTCStats 562 #[serde(with = "serialize::instant_to_epoch_seconds")] 563 pub timestamp: Instant, 564 #[serde(rename = "type")] 565 pub stats_type: RTCStatsType, 566 pub id: String, 567 568 // RTCRtpStreamStats 569 pub ssrc: SSRC, 570 pub kind: &'static str, // Either "video" or "audio" 571 // TODO: Add tranportId 572 // TODO: Add codecId 573 574 // RTCSentRtpStreamStats 575 pub packets_sent: u64, 576 pub bytes_sent: u64, 577 578 // RTCRemoteOutboundRtpStreamStats 579 pub local_id: String, 580 // TODO: `remote_timestamp` 581 pub round_trip_time: Option<f64>, 582 pub reports_sent: u64, 583 pub total_round_trip_time: f64, 584 pub round_trip_time_measurements: u64, 585 } 586