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