o_sfu_telemetry/metrics/
rtp.rs1use std::{
2 collections::BTreeMap,
3 sync::{Arc, Mutex},
4};
5
6use super::{
7 counter::{MetricLabel, PaddedCounter, PaddedCounterFamily},
8 labels::{RtpDecoderRefreshScope, RtpFlowDirection, RtpForwardDestinationKind},
9};
10
11const RTP_FORWARD_DESTINATION_COUNT: usize = <RtpForwardDestinationKind as MetricLabel>::COUNT;
12const RTP_DECODER_REFRESH_SCOPE_COUNT: usize = <RtpDecoderRefreshScope as MetricLabel>::COUNT;
13
14#[derive(Debug, Default)]
20pub struct RtpMetricsRecorder {
21 ingress_packets: PaddedCounter,
22 ingress_payload_bytes: PaddedCounter,
23 forwarded_packets: PaddedCounterFamily<RtpForwardDestinationKind>,
24 forwarded_payload_bytes: PaddedCounterFamily<RtpForwardDestinationKind>,
25 decoder_refreshes: PaddedCounterFamily<RtpDecoderRefreshScope>,
26}
27
28impl RtpMetricsRecorder {
29 pub fn record_ingress(&self, payload_bytes: usize) {
30 self.ingress_packets.increment();
31 self.ingress_payload_bytes.add(payload_bytes);
32 }
33
34 pub fn record_forwarded(&self, destination: RtpForwardDestinationKind, payload_bytes: usize) {
41 self.forwarded_packets.increment(destination);
42 self.forwarded_payload_bytes.add(destination, payload_bytes);
43 }
44
45 pub fn record_decoder_refresh(&self, scope: RtpDecoderRefreshScope) {
46 self.decoder_refreshes.increment(scope);
47 }
48}
49
50#[derive(Debug, Default)]
51pub(super) struct RtpMetrics {
52 worker_recorders: Mutex<Vec<RtpWorkerMetricsRecorder>>,
53}
54
55impl RtpMetrics {
56 pub(super) fn register_worker(
57 &self,
58 media_worker_id: Option<usize>,
59 ) -> Arc<RtpMetricsRecorder> {
60 let recorder = Arc::new(RtpMetricsRecorder::default());
61 {
62 let mut workers = match self.worker_recorders.lock() {
63 Ok(workers) => workers,
64 Err(poisoned) => poisoned.into_inner(),
65 };
66 workers.push(RtpWorkerMetricsRecorder {
67 media_worker_id,
68 recorder: Arc::clone(&recorder),
69 });
70 }
71 recorder
72 }
73
74 pub(super) fn snapshot(&self) -> RtpMetricsSnapshot {
75 let mut snapshot = RtpMetricsSnapshot::default();
76 {
77 let workers = match self.worker_recorders.lock() {
78 Ok(workers) => workers,
79 Err(poisoned) => poisoned.into_inner(),
80 };
81 let mut worker_snapshots = BTreeMap::<usize, RtpWorkerMetricsSnapshot>::new();
82 for worker in workers.iter() {
83 snapshot.add_recorder(&worker.recorder);
84 if let Some(media_worker_id) = worker.media_worker_id {
85 worker_snapshots
86 .entry(media_worker_id)
87 .or_insert_with(|| RtpWorkerMetricsSnapshot::new(media_worker_id))
88 .traffic
89 .add_recorder(&worker.recorder);
90 }
91 }
92 drop(workers);
93 snapshot.worker_snapshots = worker_snapshots.into_values().collect();
94 }
95 snapshot
96 }
97}
98
99#[derive(Debug)]
100struct RtpWorkerMetricsRecorder {
101 media_worker_id: Option<usize>,
102 recorder: Arc<RtpMetricsRecorder>,
103}
104
105#[derive(Debug, Default)]
106pub(super) struct RtpMetricsSnapshot {
107 pub(super) traffic: RtpTrafficSnapshot,
108 decoder_refreshes: [u64; RTP_DECODER_REFRESH_SCOPE_COUNT],
109 worker_snapshots: Vec<RtpWorkerMetricsSnapshot>,
110}
111
112impl RtpMetricsSnapshot {
113 pub(super) fn decoder_refreshes(&self, scope: RtpDecoderRefreshScope) -> u64 {
114 self.decoder_refreshes
115 .get(scope.as_index())
116 .copied()
117 .unwrap_or(0)
118 }
119
120 pub(super) fn worker_snapshots(&self) -> &[RtpWorkerMetricsSnapshot] {
121 &self.worker_snapshots
122 }
123
124 fn add_recorder(&mut self, recorder: &RtpMetricsRecorder) {
125 self.traffic.add_recorder(recorder);
126 recorder
127 .decoder_refreshes
128 .accumulate_into(&mut self.decoder_refreshes);
129 }
130}
131
132#[derive(Debug, Default)]
133pub(super) struct RtpWorkerMetricsSnapshot {
134 media_worker_id: usize,
135 pub(super) traffic: RtpTrafficSnapshot,
136}
137
138impl RtpWorkerMetricsSnapshot {
139 fn new(media_worker_id: usize) -> Self {
140 Self {
141 media_worker_id,
142 ..Self::default()
143 }
144 }
145
146 pub(super) const fn media_worker_id(&self) -> usize {
147 self.media_worker_id
148 }
149}
150
151#[derive(Debug, Default)]
152pub(super) struct RtpTrafficSnapshot {
153 ingress_packets: u64,
154 ingress_payload_bytes: u64,
155 forwarded_packets: [u64; RTP_FORWARD_DESTINATION_COUNT],
156 forwarded_payload_bytes: [u64; RTP_FORWARD_DESTINATION_COUNT],
157}
158
159impl RtpTrafficSnapshot {
160 pub(super) fn packets(&self, direction: RtpFlowDirection) -> u64 {
161 match direction {
162 RtpFlowDirection::Ingress => self.ingress_packets,
163 RtpFlowDirection::Egress => self.forwarded_packets(RtpForwardDestinationKind::LocalRtc),
164 }
165 }
166
167 pub(super) fn payload_bytes(&self, direction: RtpFlowDirection) -> u64 {
168 match direction {
169 RtpFlowDirection::Ingress => self.ingress_payload_bytes,
170 RtpFlowDirection::Egress => {
171 self.forwarded_payload_bytes(RtpForwardDestinationKind::LocalRtc)
172 }
173 }
174 }
175
176 pub(super) fn forwarded_packets(&self, destination: RtpForwardDestinationKind) -> u64 {
177 self.forwarded_packets
178 .get(destination.as_index())
179 .copied()
180 .unwrap_or(0)
181 }
182
183 pub(super) fn forwarded_payload_bytes(&self, destination: RtpForwardDestinationKind) -> u64 {
184 self.forwarded_payload_bytes
185 .get(destination.as_index())
186 .copied()
187 .unwrap_or(0)
188 }
189
190 fn add_recorder(&mut self, recorder: &RtpMetricsRecorder) {
191 self.ingress_packets = self
192 .ingress_packets
193 .saturating_add(recorder.ingress_packets.load());
194 self.ingress_payload_bytes = self
195 .ingress_payload_bytes
196 .saturating_add(recorder.ingress_payload_bytes.load());
197 recorder
198 .forwarded_packets
199 .accumulate_into(&mut self.forwarded_packets);
200 recorder
201 .forwarded_payload_bytes
202 .accumulate_into(&mut self.forwarded_payload_bytes);
203 }
204}