rtc_ice/candidate/
candidate_pair.rs

1use serde::{Deserialize, Serialize};
2use std::fmt;
3use std::time::Duration;
4
5/// Represent the ICE candidate pair state.
6#[derive(Default, Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
7pub enum CandidatePairState {
8    #[default]
9    #[serde(rename = "unspecified")]
10    Unspecified = 0,
11
12    /// Means a check has not been performed for this pair.
13    #[serde(rename = "waiting")]
14    Waiting = 1,
15
16    /// Means a check has been sent for this pair, but the transaction is in progress.
17    #[serde(rename = "in-progress")]
18    InProgress = 2,
19
20    /// Means a check for this pair was already done and failed, either never producing any response
21    /// or producing an unrecoverable failure response.
22    #[serde(rename = "failed")]
23    Failed = 3,
24
25    /// Means a check for this pair was already done and produced a successful result.
26    #[serde(rename = "succeeded")]
27    Succeeded = 4,
28}
29
30impl From<u8> for CandidatePairState {
31    fn from(v: u8) -> Self {
32        match v {
33            1 => Self::Waiting,
34            2 => Self::InProgress,
35            3 => Self::Failed,
36            4 => Self::Succeeded,
37            _ => Self::Unspecified,
38        }
39    }
40}
41
42impl fmt::Display for CandidatePairState {
43    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
44        let s = match *self {
45            Self::Waiting => "waiting",
46            Self::InProgress => "in-progress",
47            Self::Failed => "failed",
48            Self::Succeeded => "succeeded",
49            Self::Unspecified => "unspecified",
50        };
51
52        write!(f, "{s}")
53    }
54}
55
56/// Represents a combination of a local and remote candidate.
57#[derive(Clone, Copy)]
58pub struct CandidatePair {
59    pub local_index: usize,
60    pub remote_index: usize,
61    pub local_priority: u32,
62    pub remote_priority: u32,
63    pub(crate) ice_role_controlling: bool,
64    pub(crate) binding_request_count: u16,
65    pub(crate) state: CandidatePairState,
66    pub(crate) nominated: bool,
67
68    // STUN transaction stats
69    /// Total number of STUN connectivity check requests sent (not including retransmissions).
70    pub(crate) requests_sent: u64,
71    /// Total number of STUN connectivity check requests received.
72    pub(crate) requests_received: u64,
73    /// Total number of STUN connectivity check responses sent.
74    pub(crate) responses_sent: u64,
75    /// Total number of STUN connectivity check responses received.
76    pub(crate) responses_received: u64,
77    /// Total number of consent freshness requests sent.
78    pub(crate) consent_requests_sent: u64,
79
80    // RTT tracking
81    /// Sum of all round trip time measurements.
82    pub(crate) total_round_trip_time: Duration,
83    /// Latest round trip time measured.
84    pub(crate) current_round_trip_time: Duration,
85}
86
87impl fmt::Debug for CandidatePair {
88    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
89        write!(
90            f,
91            "prio {} (local, prio {}) {} <-> {} (remote, prio {})",
92            self.priority(),
93            self.local_priority,
94            self.local_index,
95            self.remote_index,
96            self.remote_priority,
97        )
98    }
99}
100
101impl fmt::Display for CandidatePair {
102    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
103        write!(
104            f,
105            "prio {} (local, prio {}) {} <-> {} (remote, prio {})",
106            self.priority(),
107            self.local_priority,
108            self.local_index,
109            self.remote_index,
110            self.remote_priority,
111        )
112    }
113}
114
115impl PartialEq for CandidatePair {
116    fn eq(&self, other: &Self) -> bool {
117        self.local_index == other.local_index && self.remote_index == other.remote_index
118    }
119}
120
121impl CandidatePair {
122    #[must_use]
123    pub fn new(
124        local_index: usize,
125        remote_index: usize,
126        local_priority: u32,
127        remote_priority: u32,
128        ice_role_controlling: bool,
129    ) -> Self {
130        Self {
131            local_index,
132            remote_index,
133            local_priority,
134            remote_priority,
135            ice_role_controlling,
136            state: CandidatePairState::Waiting,
137            binding_request_count: 0,
138            nominated: false,
139            // STUN transaction stats
140            requests_sent: 0,
141            requests_received: 0,
142            responses_sent: 0,
143            responses_received: 0,
144            consent_requests_sent: 0,
145            // RTT tracking
146            total_round_trip_time: Duration::ZERO,
147            current_round_trip_time: Duration::ZERO,
148        }
149    }
150
151    /// RFC 5245 - 5.7.2.  Computing Pair Priority and Ordering Pairs
152    /// Let G be the priority for the candidate provided by the controlling
153    /// agent.  Let D be the priority for the candidate provided by the
154    /// controlled agent.
155    /// pair priority = 2^32*MIN(G,D) + 2*MAX(G,D) + (G>D?1:0)
156    pub fn priority(&self) -> u64 {
157        let (g, d) = if self.ice_role_controlling {
158            (self.local_priority, self.remote_priority)
159        } else {
160            (self.remote_priority, self.local_priority)
161        };
162
163        // 1<<32 overflows uint32; and if both g && d are
164        // maxUint32, this result would overflow uint64
165        ((1 << 32_u64) - 1) * u64::from(std::cmp::min(g, d))
166            + 2 * u64::from(std::cmp::max(g, d))
167            + u64::from(g > d)
168    }
169
170    /// Called when a STUN binding request is sent.
171    pub fn on_request_sent(&mut self) {
172        self.requests_sent += 1;
173    }
174
175    /// Called when a STUN binding request is received.
176    pub fn on_request_received(&mut self) {
177        self.requests_received += 1;
178    }
179
180    /// Called when a STUN binding success response is sent.
181    pub fn on_response_sent(&mut self) {
182        self.responses_sent += 1;
183    }
184
185    /// Called when a STUN binding success response is received.
186    /// Also updates RTT measurements.
187    pub fn on_response_received(&mut self, rtt: Duration) {
188        self.responses_received += 1;
189        self.current_round_trip_time = rtt;
190        self.total_round_trip_time += rtt;
191    }
192
193    /// Called when a consent freshness request is sent (keepalive).
194    pub fn on_consent_request_sent(&mut self) {
195        self.consent_requests_sent += 1;
196    }
197}