1use std::{
2 collections::BTreeMap,
3 sync::{Arc, Mutex},
4};
5
6use super::{
7 counter::{MetricLabel, PaddedCounterFamily},
8 labels::{RtpDecoderRefreshScope, RtpFlowDirection, RtpForwardDestinationKind},
9};
10
11const RTP_FLOW_DIRECTION_COUNT: usize = <RtpFlowDirection as MetricLabel>::COUNT;
12const RTP_FORWARD_DESTINATION_COUNT: usize = <RtpForwardDestinationKind as MetricLabel>::COUNT;
13const RTP_DECODER_REFRESH_SCOPE_COUNT: usize = <RtpDecoderRefreshScope as MetricLabel>::COUNT;
14
15#[derive(Debug, Default)]
21pub struct RtpMetricsRecorder {
22 packets: PaddedCounterFamily<RtpFlowDirection>,
23 payload_bytes: PaddedCounterFamily<RtpFlowDirection>,
24 forwarded_packets: PaddedCounterFamily<RtpForwardDestinationKind>,
25 forwarded_payload_bytes: PaddedCounterFamily<RtpForwardDestinationKind>,
26 decoder_refreshes: PaddedCounterFamily<RtpDecoderRefreshScope>,
27}
28
29impl RtpMetricsRecorder {
30 pub fn record_ingress(&self, payload_bytes: usize) {
31 self.packets.increment(RtpFlowDirection::Ingress);
32 self.payload_bytes
33 .add(RtpFlowDirection::Ingress, payload_bytes);
34 }
35
36 pub fn record_egress(&self, payload_bytes: usize) {
37 self.packets.increment(RtpFlowDirection::Egress);
38 self.payload_bytes
39 .add(RtpFlowDirection::Egress, payload_bytes);
40 }
41
42 pub fn record_forwarded(&self, destination: RtpForwardDestinationKind, payload_bytes: usize) {
43 self.forwarded_packets.increment(destination);
44 self.forwarded_payload_bytes.add(destination, payload_bytes);
45 }
46
47 pub fn record_decoder_refresh(&self, scope: RtpDecoderRefreshScope) {
48 self.decoder_refreshes.increment(scope);
49 }
50}
51
52#[derive(Debug, Default)]
53pub(super) struct RtpMetrics {
54 worker_recorders: Mutex<Vec<RtpWorkerMetricsRecorder>>,
55}
56
57impl RtpMetrics {
58 pub(super) fn register_worker(
59 &self,
60 media_worker_id: Option<usize>,
61 ) -> Arc<RtpMetricsRecorder> {
62 let recorder = Arc::new(RtpMetricsRecorder::default());
63 {
64 let mut workers = match self.worker_recorders.lock() {
65 Ok(workers) => workers,
66 Err(poisoned) => poisoned.into_inner(),
67 };
68 workers.push(RtpWorkerMetricsRecorder {
69 media_worker_id,
70 recorder: Arc::clone(&recorder),
71 });
72 }
73 recorder
74 }
75
76 pub(super) fn snapshot(&self) -> RtpMetricsSnapshot {
77 let mut snapshot = RtpMetricsSnapshot::default();
78 {
79 let workers = match self.worker_recorders.lock() {
80 Ok(workers) => workers,
81 Err(poisoned) => poisoned.into_inner(),
82 };
83 let mut worker_snapshots = BTreeMap::<usize, RtpWorkerMetricsSnapshot>::new();
84 for worker in workers.iter() {
85 snapshot.add_recorder(&worker.recorder);
86 if let Some(media_worker_id) = worker.media_worker_id {
87 worker_snapshots
88 .entry(media_worker_id)
89 .or_insert_with(|| RtpWorkerMetricsSnapshot::new(media_worker_id))
90 .add_recorder(&worker.recorder);
91 }
92 }
93 drop(workers);
94 snapshot.worker_snapshots = worker_snapshots.into_values().collect();
95 }
96 snapshot
97 }
98}
99
100#[derive(Debug)]
101struct RtpWorkerMetricsRecorder {
102 media_worker_id: Option<usize>,
103 recorder: Arc<RtpMetricsRecorder>,
104}
105
106#[derive(Debug, Default)]
107pub(super) struct RtpMetricsSnapshot {
108 packets: [u64; RTP_FLOW_DIRECTION_COUNT],
109 payload_bytes: [u64; RTP_FLOW_DIRECTION_COUNT],
110 forwarded_packets: [u64; RTP_FORWARD_DESTINATION_COUNT],
111 forwarded_payload_bytes: [u64; RTP_FORWARD_DESTINATION_COUNT],
112 decoder_refreshes: [u64; RTP_DECODER_REFRESH_SCOPE_COUNT],
113 worker_snapshots: Vec<RtpWorkerMetricsSnapshot>,
114}
115
116impl RtpMetricsSnapshot {
117 pub(super) fn packets(&self, direction: RtpFlowDirection) -> u64 {
118 self.packets.get(direction.as_index()).copied().unwrap_or(0)
119 }
120
121 pub(super) fn payload_bytes(&self, direction: RtpFlowDirection) -> u64 {
122 self.payload_bytes
123 .get(direction.as_index())
124 .copied()
125 .unwrap_or(0)
126 }
127
128 pub(super) fn forwarded_packets(&self, destination: RtpForwardDestinationKind) -> u64 {
129 self.forwarded_packets
130 .get(destination.as_index())
131 .copied()
132 .unwrap_or(0)
133 }
134
135 pub(super) fn forwarded_payload_bytes(&self, destination: RtpForwardDestinationKind) -> u64 {
136 self.forwarded_payload_bytes
137 .get(destination.as_index())
138 .copied()
139 .unwrap_or(0)
140 }
141
142 pub(super) fn decoder_refreshes(&self, scope: RtpDecoderRefreshScope) -> u64 {
143 self.decoder_refreshes
144 .get(scope.as_index())
145 .copied()
146 .unwrap_or(0)
147 }
148
149 pub(super) fn worker_snapshots(&self) -> &[RtpWorkerMetricsSnapshot] {
150 &self.worker_snapshots
151 }
152
153 fn add_recorder(&mut self, recorder: &RtpMetricsRecorder) {
154 for direction in <RtpFlowDirection as MetricLabel>::VARIANTS {
155 self.add_flow(
156 *direction,
157 recorder.packets.load(*direction),
158 recorder.payload_bytes.load(*direction),
159 );
160 }
161 for destination in <RtpForwardDestinationKind as MetricLabel>::VARIANTS {
162 self.add_forwarded(
163 *destination,
164 recorder.forwarded_packets.load(*destination),
165 recorder.forwarded_payload_bytes.load(*destination),
166 );
167 }
168 for scope in <RtpDecoderRefreshScope as MetricLabel>::VARIANTS {
169 self.add_decoder_refresh(*scope, recorder.decoder_refreshes.load(*scope));
170 }
171 }
172
173 fn add_flow(&mut self, direction: RtpFlowDirection, packets: u64, payload_bytes: u64) {
174 if let Some(counter) = self.packets.get_mut(direction.as_index()) {
175 *counter = counter.saturating_add(packets);
176 }
177 if let Some(counter) = self.payload_bytes.get_mut(direction.as_index()) {
178 *counter = counter.saturating_add(payload_bytes);
179 }
180 }
181
182 fn add_forwarded(
183 &mut self,
184 destination: RtpForwardDestinationKind,
185 packets: u64,
186 payload_bytes: u64,
187 ) {
188 if let Some(counter) = self.forwarded_packets.get_mut(destination.as_index()) {
189 *counter = counter.saturating_add(packets);
190 }
191 if let Some(counter) = self.forwarded_payload_bytes.get_mut(destination.as_index()) {
192 *counter = counter.saturating_add(payload_bytes);
193 }
194 }
195
196 fn add_decoder_refresh(&mut self, scope: RtpDecoderRefreshScope, refreshes: u64) {
197 if let Some(counter) = self.decoder_refreshes.get_mut(scope.as_index()) {
198 *counter = counter.saturating_add(refreshes);
199 }
200 }
201}
202
203#[derive(Debug, Default)]
204pub(super) struct RtpWorkerMetricsSnapshot {
205 media_worker_id: usize,
206 packets: [u64; RTP_FLOW_DIRECTION_COUNT],
207 payload_bytes: [u64; RTP_FLOW_DIRECTION_COUNT],
208 forwarded_packets: [u64; RTP_FORWARD_DESTINATION_COUNT],
209 forwarded_payload_bytes: [u64; RTP_FORWARD_DESTINATION_COUNT],
210}
211
212impl RtpWorkerMetricsSnapshot {
213 fn new(media_worker_id: usize) -> Self {
214 Self {
215 media_worker_id,
216 ..Self::default()
217 }
218 }
219
220 pub(super) const fn media_worker_id(&self) -> usize {
221 self.media_worker_id
222 }
223
224 pub(super) fn packets(&self, direction: RtpFlowDirection) -> u64 {
225 self.packets.get(direction.as_index()).copied().unwrap_or(0)
226 }
227
228 pub(super) fn payload_bytes(&self, direction: RtpFlowDirection) -> u64 {
229 self.payload_bytes
230 .get(direction.as_index())
231 .copied()
232 .unwrap_or(0)
233 }
234
235 pub(super) fn forwarded_packets(&self, destination: RtpForwardDestinationKind) -> u64 {
236 self.forwarded_packets
237 .get(destination.as_index())
238 .copied()
239 .unwrap_or(0)
240 }
241
242 pub(super) fn forwarded_payload_bytes(&self, destination: RtpForwardDestinationKind) -> u64 {
243 self.forwarded_payload_bytes
244 .get(destination.as_index())
245 .copied()
246 .unwrap_or(0)
247 }
248
249 fn add_recorder(&mut self, recorder: &RtpMetricsRecorder) {
250 for direction in <RtpFlowDirection as MetricLabel>::VARIANTS {
251 self.add_flow(
252 *direction,
253 recorder.packets.load(*direction),
254 recorder.payload_bytes.load(*direction),
255 );
256 }
257 for destination in <RtpForwardDestinationKind as MetricLabel>::VARIANTS {
258 self.add_forwarded(
259 *destination,
260 recorder.forwarded_packets.load(*destination),
261 recorder.forwarded_payload_bytes.load(*destination),
262 );
263 }
264 }
265
266 fn add_flow(&mut self, direction: RtpFlowDirection, packets: u64, payload_bytes: u64) {
267 if let Some(counter) = self.packets.get_mut(direction.as_index()) {
268 *counter = counter.saturating_add(packets);
269 }
270 if let Some(counter) = self.payload_bytes.get_mut(direction.as_index()) {
271 *counter = counter.saturating_add(payload_bytes);
272 }
273 }
274
275 fn add_forwarded(
276 &mut self,
277 destination: RtpForwardDestinationKind,
278 packets: u64,
279 payload_bytes: u64,
280 ) {
281 if let Some(counter) = self.forwarded_packets.get_mut(destination.as_index()) {
282 *counter = counter.saturating_add(packets);
283 }
284 if let Some(counter) = self.forwarded_payload_bytes.get_mut(destination.as_index()) {
285 *counter = counter.saturating_add(payload_bytes);
286 }
287 }
288}