Skip to main content

sipx_media/
counters.rs

1//! Plain snapshots of media-path discards (`docs/specs/media-runtime.md` ยง4).
2
3use std::sync::atomic::{AtomicU64, Ordering};
4
5/// What one media session discarded, split by consequence.
6///
7/// Each field is exact and monotonic. The snapshot as a whole is not instantaneous: independent
8/// workers can increment fields between these loads, so relationships between fields are exact
9/// only while the session is quiet.
10#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
11pub struct MediaDiscardCounts {
12    /// Audio frames an Opus encoder refused.
13    pub opus_encode_failures: u64,
14    /// Opus packets the decoder refused.
15    pub opus_decode_failures: u64,
16    /// RTP packets that failed SRTP authentication or decryption.
17    pub srtp_unprotect_failures: u64,
18    /// RTCP reports that failed SRTCP authentication or decryption.
19    pub srtcp_unprotect_failures: u64,
20    /// RTP packets whose SSRC differed from the established stream.
21    pub foreign_ssrc: u64,
22    /// Complete DTMF digits refused because the application queue was full or closed.
23    pub dtmf_delivery_failures: u64,
24    /// RTP packets carrying neither the negotiated payload type nor a known static codec.
25    pub unknown_payload_type: u64,
26    /// Playback completion reports whose last observer had gone away.
27    pub playback_completion_unobserved: u64,
28    /// Connectivity checks refused by a full or closed ICE-driver queue.
29    pub ice_driver_queue_refusals: u64,
30    /// Media-sent notes refused by a full or closed ICE-driver queue.
31    pub ice_data_sent_queue_refusals: u64,
32    /// ICE renegotiation replies whose requester had stopped waiting.
33    pub ice_renegotiation_reply_unobserved: u64,
34    /// ICE connectivity checks the socket refused to send.
35    pub ice_send_failures: u64,
36    /// Server-reflexive candidates discarded because they duplicate a host candidate.
37    pub ice_redundant_candidates: u64,
38    /// Datagrams consumed during gathering that did not come from the queried STUN server.
39    pub ice_gathering_foreign_datagrams: u64,
40}
41
42impl MediaDiscardCounts {
43    /// Whether this session has discarded anything.
44    #[must_use]
45    pub fn any(self) -> bool {
46        self.total() > 0
47    }
48
49    /// All counted media discards, saturating rather than wrapping back to a plausible zero.
50    #[must_use]
51    pub fn total(self) -> u64 {
52        [
53            self.opus_encode_failures,
54            self.opus_decode_failures,
55            self.srtp_unprotect_failures,
56            self.srtcp_unprotect_failures,
57            self.foreign_ssrc,
58            self.dtmf_delivery_failures,
59            self.unknown_payload_type,
60            self.playback_completion_unobserved,
61            self.ice_driver_queue_refusals,
62            self.ice_data_sent_queue_refusals,
63            self.ice_renegotiation_reply_unobserved,
64            self.ice_send_failures,
65            self.ice_redundant_candidates,
66            self.ice_gathering_foreign_datagrams,
67        ]
68        .into_iter()
69        .fold(0, u64::saturating_add)
70    }
71}
72
73/// The live form shared by every worker belonging to one session.
74#[derive(Debug, Default)]
75pub(crate) struct DiscardMeters {
76    pub(crate) opus_encode_failures: AtomicU64,
77    pub(crate) opus_decode_failures: AtomicU64,
78    pub(crate) srtp_unprotect_failures: AtomicU64,
79    pub(crate) srtcp_unprotect_failures: AtomicU64,
80    pub(crate) foreign_ssrc: AtomicU64,
81    pub(crate) dtmf_delivery_failures: AtomicU64,
82    pub(crate) unknown_payload_type: AtomicU64,
83    pub(crate) playback_completion_unobserved: AtomicU64,
84    pub(crate) ice_driver_queue_refusals: AtomicU64,
85    pub(crate) ice_data_sent_queue_refusals: AtomicU64,
86    pub(crate) ice_renegotiation_reply_unobserved: AtomicU64,
87    pub(crate) ice_send_failures: AtomicU64,
88    pub(crate) ice_redundant_candidates: AtomicU64,
89    pub(crate) ice_gathering_foreign_datagrams: AtomicU64,
90}
91
92impl DiscardMeters {
93    pub(crate) fn snapshot(&self) -> MediaDiscardCounts {
94        MediaDiscardCounts {
95            opus_encode_failures: self.opus_encode_failures.load(Ordering::Relaxed),
96            opus_decode_failures: self.opus_decode_failures.load(Ordering::Relaxed),
97            srtp_unprotect_failures: self.srtp_unprotect_failures.load(Ordering::Relaxed),
98            srtcp_unprotect_failures: self.srtcp_unprotect_failures.load(Ordering::Relaxed),
99            foreign_ssrc: self.foreign_ssrc.load(Ordering::Relaxed),
100            dtmf_delivery_failures: self.dtmf_delivery_failures.load(Ordering::Relaxed),
101            unknown_payload_type: self.unknown_payload_type.load(Ordering::Relaxed),
102            playback_completion_unobserved: self
103                .playback_completion_unobserved
104                .load(Ordering::Relaxed),
105            ice_driver_queue_refusals: self.ice_driver_queue_refusals.load(Ordering::Relaxed),
106            ice_data_sent_queue_refusals: self.ice_data_sent_queue_refusals.load(Ordering::Relaxed),
107            ice_renegotiation_reply_unobserved: self
108                .ice_renegotiation_reply_unobserved
109                .load(Ordering::Relaxed),
110            ice_send_failures: self.ice_send_failures.load(Ordering::Relaxed),
111            ice_redundant_candidates: self.ice_redundant_candidates.load(Ordering::Relaxed),
112            ice_gathering_foreign_datagrams: self
113                .ice_gathering_foreign_datagrams
114                .load(Ordering::Relaxed),
115        }
116    }
117}