Skip to main content

sipx_media/
session.rs

1//! A media session: RTP sockets, paced sending, and buffered receiving.
2//!
3//! Three decisions shape this.
4//!
5//! **Symmetric RTP.** Media is sent back to where it arrives from, not to the address the SDP
6//! advertised. Behind a NAT the advertised address is a private one and the only path back is
7//! the pinhole the far end opened by sending. The SDP address is used until the first packet
8//! arrives, then the observed source wins.
9//!
10//! **The clock lives in one place.** Audio is paced by a single interval timer at the
11//! packetisation interval. Sending on a channel's readiness instead makes the packet rate
12//! depend on how fast the application produces samples, which is how a call ends up sending
13//! 200 packets per second to a jitter buffer expecting 50.
14//!
15//! **Mute substitutes silence; it does not stop the stream** ([`MediaSession::set_muted`], story
16//! `M-18`). A muted session sends exactly the packets it would have sent unmuted, on the same
17//! pacing, sequence numbers and timestamps, with the audio replaced by encoded silence. The
18//! alternative — suppressing the packets while muted — was rejected on three counts: it closes
19//! the NAT pinhole and invites a media-inactivity teardown on any path with an SBC in it; it
20//! leaves the far end's jitter buffer to restart on unmute, so the first word after it is the one
21//! that gets clipped; and it makes "muted" indistinguishable on the wire from "the far end has
22//! gone away", which is the one thing a receiver most needs to be able to tell apart.
23//!
24//! **Playback is a queue with a handle on it** ([`MediaSession::start_playback`], story `M-17`).
25//! Clips are played in the order they were started, one at a time; a second clip started while
26//! one is running waits behind it rather than replacing it. Stopping is the explicit verb, and it
27//! reaches into the send path: a stopped clip's frames are dropped as the send loop takes them
28//! off the queue, so a stop costs at most [`Playback::STOP_BOUND_PACKETS`] packets on the wire
29//! rather than however many the queue happened to be holding.
30//!
31//! **The RFC 3550 §6 consequence, either way, is that the reports must stay truthful**, and that
32//! is what fixes *where* the gate goes rather than what it does. A sender report's packet and
33//! octet counts (§6.4.1) describe what this side put on the wire, and the far end's loss estimate
34//! is computed from the sequence numbers it received against the ones it expected. So the gate
35//! sits **before the packet is built**: what goes out is counted, what is counted went out, and
36//! the sequence space advances once per packet sent. A mute implemented one step later — building
37//! the packet, then discarding the datagram — would make this side's own reports overstate what
38//! it sent *and* manufacture a burst of apparent loss at the far end out of a caller who was
39//! merely quiet. Silence substitution keeps the numbers describing a stream that never stopped;
40//! had suppression been chosen, the same rule would have required the counters and the sequence
41//! number to stay put for the duration.
42//!
43//! Dropping a stopped clip's frames is not the case that rule forbids, and the difference is
44//! worth being exact about. A mute is a session that is *still talking* and must go on saying
45//! something; a stopped playback is a session with **nothing left to say**, which is the state a
46//! session is in whenever the application is not feeding it — the send loop simply parks on its
47//! queue. So the counters and the sequence number stay put, exactly as they do between clips, and
48//! what a stop leaves behind is silence in the ordinary sense: no packets, no gap, nothing for a
49//! receiver to score.
50
51use std::net::SocketAddr;
52use std::sync::Arc;
53use std::sync::atomic::{AtomicBool, AtomicU64, AtomicUsize, Ordering};
54use std::time::Duration;
55
56use bytes::Bytes;
57use sipx_audio::g711;
58use sipx_rtp::dtmf::{self, Digit, Event as DtmfEvent};
59use sipx_rtp::rtcp::{ReceiverReport, Rtcp, Sdes, StreamStats};
60use sipx_rtp::{JitterBuffer, Packet};
61use sipx_sdp::ice::ComponentId;
62use tokio::net::UdpSocket;
63use tokio::sync::{Mutex, mpsc, watch};
64
65use crate::counters::{DiscardMeters, MediaDiscardCounts};
66use crate::ice;
67
68/// Which G.711 flavour a session carries.
69#[derive(Debug, Clone, Copy, PartialEq, Eq)]
70pub enum Codec {
71    /// µ-law, payload type 0.
72    Pcmu,
73    /// A-law, payload type 8.
74    Pcma,
75    /// Signed 16-bit network-order linear PCM (RFC 3551 §4.5.11).
76    ///
77    /// Static payload type 11 is mono at 44.1 kHz. Other negotiated rates use a dynamic payload
78    /// assignment and override [`Config::clock_rate`].
79    L16,
80    /// Opus (RFC 6716), on whatever dynamic payload type was negotiated.
81    ///
82    /// Unlike the G.711 pair this carries *state*: an Opus encoder and decoder each hold a
83    /// model of the signal they have seen, which is how the codec achieves what it does and why
84    /// it cannot be a pure function of one frame. The state lives in the send and receive
85    /// loops, one each, so nothing is shared and nothing is locked.
86    #[cfg(feature = "opus")]
87    Opus,
88}
89
90/// Which half of a negotiated codec could not be constructed.
91#[derive(Debug, Clone, Copy, PartialEq, Eq)]
92pub enum CodecDirection {
93    /// The encoder for media sent to the peer.
94    Encoder,
95    /// The decoder for media received from the peer.
96    Decoder,
97}
98
99impl std::fmt::Display for CodecDirection {
100    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
101        match self {
102            Self::Encoder => f.write_str("encoder"),
103            Self::Decoder => f.write_str("decoder"),
104        }
105    }
106}
107
108/// A negotiated media session that cannot be constructed safely.
109#[derive(Debug, thiserror::Error)]
110#[non_exhaustive]
111pub enum SetupError {
112    /// Packet pacing cannot represent a frame shorter than one millisecond.
113    #[error("packet duration must be at least 1 ms, got {0:?}")]
114    PacketDurationTooShort(Duration),
115    /// A configured RTCP timer must make forward progress.
116    #[error("RTCP interval must be at least 1 ms, got {0:?}")]
117    RtcpIntervalTooShort(Duration),
118    /// RFC 5761 cannot distinguish this marked RTP payload from an RTCP packet type.
119    #[error("RTP payload type {0} collides with RTCP while rtcp-mux is active")]
120    RtcpMuxPayloadCollision(u8),
121    /// The codec agreed through SDP could not create one of its stateful directions.
122    #[error("cannot construct {codec:?} {direction}: {reason}")]
123    Codec {
124        /// The negotiated wire codec.
125        codec: Codec,
126        /// Whether setup failed for sending or receiving.
127        direction: CodecDirection,
128        /// The codec library's diagnostic. It contains no media or key material.
129        reason: String,
130    },
131}
132
133/// Binding or constructing a media session failed.
134#[derive(Debug, thiserror::Error)]
135#[non_exhaustive]
136pub enum StartError {
137    /// A socket could not be bound.
138    #[error("io: {0}")]
139    Io(#[from] std::io::Error),
140    /// The negotiated session could not be constructed.
141    #[error(transparent)]
142    Setup(#[from] SetupError),
143}
144
145/// A DTLS handshake could not take a bound media port into an SRTP session.
146#[cfg(feature = "dtls")]
147#[derive(Debug, thiserror::Error)]
148#[non_exhaustive]
149pub enum DtlsStartError {
150    /// The media socket could not be converted or configured for the handshake.
151    #[error("io: {0}")]
152    Io(#[from] std::io::Error),
153    /// Something else retained the not-yet-started socket.
154    #[error("the media socket is already shared")]
155    SocketShared,
156    /// Certificate verification, profile negotiation or key export failed.
157    #[error("{0}")]
158    Handshake(#[from] crate::dtls::Error),
159    /// The bounded blocking handshake worker did not return normally.
160    #[error("the DTLS handshake worker failed: {0}")]
161    Worker(String),
162}
163
164impl Codec {
165    /// The static payload type.
166    #[must_use]
167    pub fn payload_type(self) -> u8 {
168        match self {
169            Self::Pcmu => 0,
170            Self::Pcma => 8,
171            Self::L16 => 11,
172            // Opus has no static type — RFC 7587 §7 assigns none — so 111 is convention and
173            // nothing more. What goes on the wire is whatever SDP negotiated, which
174            // [`Config::payload_type`] carries.
175            #[cfg(feature = "opus")]
176            Self::Opus => 111,
177        }
178    }
179
180    /// The RTP clock rate, which is not always the sample rate.
181    ///
182    /// RFC 7587 §7 fixes Opus's RTP clock at 48000 whatever the audio is sampled at. A stack
183    /// that used the sample rate instead produces timestamps the far end reads at the wrong
184    /// speed.
185    #[must_use]
186    pub fn clock_rate(self) -> u32 {
187        match self {
188            Self::Pcmu | Self::Pcma => 8_000,
189            Self::L16 => 44_100,
190            #[cfg(feature = "opus")]
191            Self::Opus => sipx_audio::opus::CLOCK_RATE,
192        }
193    }
194
195    /// The codec for a payload type, if it is one we carry.
196    #[must_use]
197    pub fn from_payload_type(payload_type: u8) -> Option<Self> {
198        match payload_type {
199            0 => Some(Self::Pcmu),
200            8 => Some(Self::Pcma),
201            // RFC 3551 §6: 11 is mono L16 at 44.1 kHz. Type 10 is stereo, which this mono
202            // application boundary deliberately does not claim.
203            11 => Some(Self::L16),
204            // Deliberately never Opus. A dynamic payload type means whatever `a=rtpmap` said,
205            // and the number alone means nothing: guessing Opus from 111 would decode somebody
206            // else's G.729 as Opus. The negotiated number lives on the session's config.
207            _ => None,
208        }
209    }
210
211    fn encode(self, samples: &[i16]) -> Vec<u8> {
212        match self {
213            Self::Pcmu => g711::ulaw_encode_all(samples),
214            Self::Pcma => g711::alaw_encode_all(samples),
215            Self::L16 => sipx_audio::l16::encode(samples),
216            // Unreachable: an Opus session encodes through [`Encoding`], which holds the state
217            // this signature has nowhere to put.
218            #[cfg(feature = "opus")]
219            Self::Opus => Vec::new(),
220        }
221    }
222
223    fn decode(self, payload: &[u8]) -> Option<Vec<i16>> {
224        match self {
225            Self::Pcmu => Some(g711::ulaw_decode_all(payload)),
226            Self::Pcma => Some(g711::alaw_decode_all(payload)),
227            Self::L16 => sipx_audio::l16::decode(payload).ok(),
228            #[cfg(feature = "opus")]
229            Self::Opus => None,
230        }
231    }
232}
233
234/// Encoding for one outgoing stream.
235///
236/// Owned by the send loop, which is the whole design: a stateful codec behind a lock would put
237/// a mutex in the packet path for no reason, since exactly one task ever encodes.
238#[derive(Debug)]
239enum Encoding {
240    /// Stateless — a pure function of the samples.
241    Direct(Codec),
242    #[cfg(feature = "opus")]
243    Opus(Box<sipx_audio::opus::Encoder>),
244}
245
246impl Encoding {
247    #[cfg_attr(not(feature = "opus"), allow(clippy::unnecessary_wraps))]
248    fn for_codec(codec: Codec, channels: usize) -> Result<Self, SetupError> {
249        match codec {
250            #[cfg(feature = "opus")]
251            Codec::Opus => match sipx_audio::opus::Encoder::new(channels) {
252                Ok(encoder) => Ok(Self::Opus(Box::new(encoder))),
253                Err(error) => Err(SetupError::Codec {
254                    codec,
255                    direction: CodecDirection::Encoder,
256                    reason: error.to_string(),
257                }),
258            },
259            other => {
260                // discard: `channels` exists only for the feature-gated stateful codec.
261                let _ = channels;
262                Ok(Self::Direct(other))
263            }
264        }
265    }
266
267    // `None` is unreachable without the `opus` feature, because G.711 cannot refuse a frame.
268    // The signature stays fallible in both builds so the send loop is one piece of code rather
269    // than two that have to be kept saying the same thing.
270    #[cfg_attr(not(feature = "opus"), allow(clippy::unnecessary_wraps))]
271    fn encode(&mut self, samples: &[i16]) -> Option<Vec<u8>> {
272        match self {
273            Self::Direct(codec) => Some(codec.encode(samples)),
274            #[cfg(feature = "opus")]
275            Self::Opus(encoder) => match encoder.encode(samples) {
276                Ok(packet) => Some(packet),
277                Err(error) => {
278                    // discard: the send loop counts the `None` returned from this callback.
279                    tracing::debug!(%error, "dropping a frame Opus could not encode");
280                    None
281                }
282            },
283        }
284    }
285}
286
287/// Decoding for one incoming stream. Owned by the receive loop, for the same reason.
288#[derive(Debug)]
289enum Decoding {
290    Direct(Codec),
291    #[cfg(feature = "opus")]
292    Opus(Box<sipx_audio::opus::Decoder>),
293}
294
295impl Decoding {
296    #[cfg_attr(not(feature = "opus"), allow(clippy::unnecessary_wraps))]
297    fn for_codec(codec: Codec, channels: usize) -> Result<Self, SetupError> {
298        match codec {
299            #[cfg(feature = "opus")]
300            Codec::Opus => match sipx_audio::opus::Decoder::new(channels) {
301                Ok(decoder) => Ok(Self::Opus(Box::new(decoder))),
302                Err(error) => Err(SetupError::Codec {
303                    codec,
304                    direction: CodecDirection::Decoder,
305                    reason: error.to_string(),
306                }),
307            },
308            other => {
309                // discard: `channels` exists only for the feature-gated stateful codec.
310                let _ = channels;
311                Ok(Self::Direct(other))
312            }
313        }
314    }
315
316    #[cfg_attr(not(feature = "opus"), allow(clippy::unnecessary_wraps))]
317    fn decode(&mut self, payload: &[u8]) -> Option<Vec<i16>> {
318        match self {
319            Self::Direct(codec) => codec.decode(payload),
320            #[cfg(feature = "opus")]
321            Self::Opus(decoder) => match decoder.decode(payload) {
322                Ok(samples) => Some(samples),
323                Err(error) => {
324                    // A packet the codec rejects is dropped, not played. A decoder pushed past
325                    // a malformed packet produces noise, and noise is louder than a gap.
326                    // discard: `deliver` counts the `None` returned from this callback.
327                    tracing::debug!(%error, "dropping a packet Opus could not decode");
328                    None
329                }
330            },
331        }
332    }
333}
334
335/// How a session is configured.
336#[derive(Debug, Clone)]
337pub struct Config {
338    /// Where to send until symmetric RTP learns better.
339    pub remote: SocketAddr,
340    /// Which codec.
341    pub codec: Codec,
342    /// The payload type to send with, when the codec's own is not the negotiated one.
343    ///
344    /// `None` uses [`Codec::payload_type`]. A *dynamic* codec has no number of its own — Opus
345    /// has none at all (RFC 7587 §7) — so the number comes from the `a=rtpmap` the two sides
346    /// agreed on, and it may differ from the one sipx would have proposed. Assuming otherwise
347    /// sends audio on a number the far end has assigned to something else.
348    pub payload_type: Option<u8>,
349    /// The payload type accepted for this codec when our description assigned a different
350    /// dynamic number from the peer's.
351    ///
352    /// `None` mirrors [`Self::wire_payload_type`], which preserves the ordinary symmetric case.
353    /// Offer/answer may nevertheless assign the same codec independently in each direction:
354    /// outgoing packets use the peer's number and incoming packets use ours (RFC 3264 §6.1).
355    pub receive_payload_type: Option<u8>,
356    /// How many channels the codec carries. One, for telephony.
357    pub channels: usize,
358    /// SRTP keys, if the media is to be encrypted (RFC 3711).
359    ///
360    /// `None` sends and expects plain RTP. There is deliberately no middle setting — no "accept
361    /// either" — because a session that falls back to cleartext when a packet fails to
362    /// authenticate is a session an attacker can downgrade by sending one bad packet.
363    pub srtp: Option<SrtpKeys>,
364    /// How much audio each packet carries. 20 ms is universal; values below 1 ms are rejected
365    /// by [`Self::validate`] and every session-start API.
366    pub packet_duration: Duration,
367    /// RTP-clock samples per second for the exact negotiated format.
368    ///
369    /// G.711 is always 8000; L16 may use its static 44100 clock or a dynamically mapped rate.
370    pub clock_rate: u32,
371    /// How many packets the jitter buffer holds, and never fewer.
372    pub jitter_depth: usize,
373    /// The deepest it may grow when the network misbehaves, in packets.
374    ///
375    /// `None` fixes the depth at [`Self::jitter_depth`], which is what the comparison tests in
376    /// `sipx-rtp` measure the adaptive buffer against. Adapting is the default because being
377    /// too shallow is audible and being too deep is not — but the ceiling is a real ceiling: a
378    /// call with a quarter-second of delay is still a call, and one with three seconds is not.
379    pub jitter_max_depth: Option<usize>,
380    /// How often to send RTCP receiver reports. `None` disables RTCP entirely; a configured
381    /// interval must be at least 1 ms.
382    ///
383    /// RFC 3550 §6.2 scales the interval with the session's bandwidth and membership; for a
384    /// two-party call that arithmetic lands at the five-second minimum, so sipx uses it
385    /// directly rather than implementing a calculation that would always return the same
386    /// answer.
387    pub rtcp_interval: Option<Duration>,
388    /// Whether RTCP uses the RTP socket or its adjacent control socket (RFC 5761).
389    pub rtcp_mode: sipx_sdp::RtcpMode,
390    /// The payload type carrying `telephone-event`, if the SDP negotiated one.
391    ///
392    /// It is dynamic, so the number is whatever the answer said — assuming 101 because that
393    /// is what sipx offers would decode another endpoint's codec as keypresses.
394    pub dtmf_payload_type: Option<u8>,
395}
396
397impl Config {
398    const MIN_INTERVAL: Duration = Duration::from_millis(1);
399
400    /// The payload type this session puts on the wire.
401    #[must_use]
402    pub fn wire_payload_type(&self) -> u8 {
403        self.payload_type
404            .unwrap_or_else(|| self.codec.payload_type())
405    }
406
407    /// The payload type this session accepts for its negotiated codec.
408    #[must_use]
409    pub fn receive_wire_payload_type(&self) -> u8 {
410        self.receive_payload_type
411            .unwrap_or_else(|| self.wire_payload_type())
412    }
413
414    /// A session to a peer in this codec, with the settings everything uses.
415    #[must_use]
416    pub fn new(remote: SocketAddr, codec: Codec) -> Self {
417        Self {
418            remote,
419            codec,
420            payload_type: None,
421            receive_payload_type: None,
422            channels: 1,
423            srtp: None,
424            packet_duration: Duration::from_millis(20),
425            clock_rate: codec.clock_rate(),
426            jitter_depth: 3,
427            jitter_max_depth: Some(12),
428            rtcp_interval: Some(Duration::from_secs(5)),
429            rtcp_mode: sipx_sdp::RtcpMode::Separate,
430            dtmf_payload_type: Some(dtmf::DEFAULT_PAYLOAD_TYPE),
431        }
432    }
433
434    /// How many samples one packet carries.
435    #[must_use]
436    pub fn samples_per_packet(&self) -> usize {
437        let millis = u64::try_from(self.packet_duration.as_millis()).unwrap_or(20);
438        usize::try_from(u64::from(self.clock_rate) * millis / 1000).unwrap_or(160)
439    }
440
441    /// Check the values used by worker timers before any worker or socket starts.
442    ///
443    /// # Errors
444    ///
445    /// Returns [`SetupError::PacketDurationTooShort`] or
446    /// [`SetupError::RtcpIntervalTooShort`] for a duration below one millisecond.
447    pub fn validate(&self) -> Result<(), SetupError> {
448        if self.packet_duration < Self::MIN_INTERVAL {
449            return Err(SetupError::PacketDurationTooShort(self.packet_duration));
450        }
451        if let Some(interval) = self.rtcp_interval
452            && interval < Self::MIN_INTERVAL
453        {
454            return Err(SetupError::RtcpIntervalTooShort(interval));
455        }
456        if self.rtcp_mode == sipx_sdp::RtcpMode::Mux {
457            for payload in [
458                Some(self.wire_payload_type()),
459                Some(self.receive_wire_payload_type()),
460                self.dtmf_payload_type,
461            ]
462            .into_iter()
463            .flatten()
464            {
465                if (64..=95).contains(&payload) {
466                    return Err(SetupError::RtcpMuxPayloadCollision(payload));
467                }
468            }
469        }
470        Ok(())
471    }
472}
473
474/// Everything that can fail before a session's first worker is spawned.
475struct Prepared {
476    encoding: Encoding,
477    decoding: Decoding,
478}
479
480impl Prepared {
481    fn new(config: &Config) -> Result<Self, SetupError> {
482        config.validate()?;
483        // Construct both directions before spawning either. A half-started negotiated codec is
484        // not a media session, and substituting another codec would break the payload contract.
485        let encoding = Encoding::for_codec(config.codec, config.channels)?;
486        let decoding = Decoding::for_codec(config.codec, config.channels)?;
487        Ok(Self { encoding, decoding })
488    }
489}
490
491/// What the paced send queue carries.
492///
493/// Audio and DTMF share one queue because they share one clock and one sequence number space.
494/// A separate path for events would have to interleave them anyway, and would get the
495/// sequence numbering wrong the first time both were busy.
496#[derive(Debug)]
497enum Frame {
498    /// One packet's worth of samples.
499    ///
500    /// `playback` is the stop signal of the clip this frame belongs to, when it belongs to one
501    /// (`M-17`). The send loop reads it and drops the frame if that playback has been stopped,
502    /// which is what makes a stop cost a bounded number of packets rather than the whole depth of
503    /// this queue. `None` for a frame the application sent directly through
504    /// [`MediaSession::send`], which nothing can cancel.
505    Audio {
506        samples: Vec<i16>,
507        playback: Option<Arc<Stop>>,
508    },
509    /// One telephone event, tagged with the keypress it belongs to.
510    ///
511    /// The tag is what holds a tone together. Every packet of one keypress must carry the same
512    /// RTP timestamp, including the three end retransmissions — and the send loop cannot tell
513    /// from an end packet alone whether more of them are coming. Without the tag it started a
514    /// new tone on each retransmission, and one keypress arrived as three digits.
515    ///
516    /// `offset` is the packet's segment start within the event: zero until a keypress
517    /// outlives the 16-bit duration field, after which each further segment stamps its
518    /// packets that much past the event's start (RFC 4733 §2.5.1.3).
519    Dtmf {
520        event: DtmfEvent,
521        offset: u32,
522        tone: u64,
523    },
524    /// Payload to put on the wire exactly as given.
525    ///
526    /// For a bridge between two calls that agreed on the same codec. G.711 survives a decode
527    /// and re-encode exactly, so for the codec sipx ships today this saves work rather than
528    /// quality; for any codec whose decode is not invertible it saves both. See
529    /// [`crate::bridge`].
530    Encoded { payload_type: u8, payload: Bytes },
531}
532
533/// The master keys for one SRTP session, one direction each.
534///
535/// Separate directions because RFC 3711 keys them separately: each side offers its own key in
536/// SDP and uses the other's to decrypt. Sharing one key between directions would give both ends
537/// the same keystream for the same packet index, which is the classic way to lose a stream
538/// cipher.
539#[derive(Clone, PartialEq, Eq)]
540pub struct SrtpKeys {
541    /// Master key and salt this side encrypts with — the one offered in our SDP.
542    pub local: (Vec<u8>, Vec<u8>),
543    /// Master key and salt the far end encrypts with — the one from its SDP.
544    pub remote: (Vec<u8>, Vec<u8>),
545}
546
547impl SrtpKeys {
548    /// The keys an SDES answer settled on, **after** checking it against what was offered
549    /// (RFC 4568 §5.1.3; `docs/specs/srtp.md` §5.4).
550    ///
551    /// This is the seam between the signalling and the media path, and the reason it is fallible
552    /// rather than an `Option`: an answer that echoed a tag this side never sent has agreed to
553    /// nothing, and the two outcomes that are not an error are both worse than one. Returning
554    /// `None` would place the call unencrypted, so a user who asked for a secure call gets an
555    /// insecure one and nothing says so; dropping the stream would end the call with no reason
556    /// anyone can act on. The error carries which tag came back and why it was refused.
557    ///
558    /// `answered` is `None` when the answer carried no `a=crypto` this side can perform — the
559    /// shape in which "a suite that was never offered" arrives, since
560    /// [`sipx_sdp::crypto::Crypto::parse`] refuses one sipx cannot key.
561    ///
562    /// # Errors
563    ///
564    /// [`sipx_sdp::SdpError::Invalid`] when the answer accepted a tag and suite pair that was
565    /// never offered, or carried no key. It never names key material.
566    pub fn from_answer(
567        offered: &[sipx_sdp::crypto::Crypto],
568        answered: Option<&sipx_sdp::crypto::Crypto>,
569    ) -> Result<Self, sipx_sdp::SdpError> {
570        let ours = sipx_sdp::crypto::Crypto::verify_answer(offered, answered)?;
571        // `verify_answer` returning `Ok` is what makes this `answered` usable at all, so the
572        // far half is read only here and never from an answer that was not checked.
573        let theirs = answered.ok_or(sipx_sdp::SdpError::Invalid {
574            field: "crypto",
575            value: "the answer carried no crypto attribute this side can perform".to_owned(),
576        })?;
577        Ok(Self {
578            local: (ours.master_key().to_vec(), ours.master_salt().to_vec()),
579            remote: (theirs.master_key().to_vec(), theirs.master_salt().to_vec()),
580        })
581    }
582}
583
584impl std::fmt::Debug for SrtpKeys {
585    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
586        // Keys. A derived `Debug` puts them in whatever log the caller writes.
587        f.write_str("SrtpKeys { .. }")
588    }
589}
590
591/// A packet's payload as it arrived, still encoded.
592#[derive(Debug, Clone)]
593pub struct Encoded {
594    /// What it is encoded in.
595    pub payload_type: u8,
596    /// The bytes.
597    pub payload: Bytes,
598}
599
600/// One peer RTCP report block describing this session's outbound RTP stream.
601#[derive(Debug, Clone, Copy, PartialEq)]
602pub struct RtcpQualitySample {
603    /// SSRC of the peer that sent the sender or receiver report.
604    pub reporter_ssrc: u32,
605    /// Local stream SSRC named by the report block.
606    pub stream_ssrc: u32,
607    /// Loss in this report interval, between zero and one.
608    pub loss: f64,
609    /// Packets lost since the peer began observing the stream.
610    pub cumulative_lost: i32,
611    /// Peer-observed interarrival jitter in time rather than RTP timestamp units.
612    pub jitter: Duration,
613    /// Round-trip time derived from `LSR` and `DLSR`, when the report carries a usable echo.
614    pub round_trip: Option<Duration>,
615}
616
617/// Application-owned handling for peer RTCP quality reports.
618///
619/// The callback runs on the RTCP receive worker after parsing and outside sipx locks. It must
620/// return promptly; applications that do blocking export put a bounded queue behind it. sipx
621/// catches a callback panic so application code cannot terminate the media worker.
622#[derive(Clone)]
623pub struct RtcpQualityHook(Arc<dyn Fn(RtcpQualitySample) + Send + Sync + 'static>);
624
625impl RtcpQualityHook {
626    /// Wrap an application callback.
627    pub fn new(callback: impl Fn(RtcpQualitySample) + Send + Sync + 'static) -> Self {
628        Self(Arc::new(callback))
629    }
630
631    fn observe(&self, sample: RtcpQualitySample) {
632        (self.0)(sample);
633    }
634}
635
636impl std::fmt::Debug for RtcpQualityHook {
637    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
638        f.write_str("RtcpQualityHook { .. }")
639    }
640}
641
642type QualityHookSlot = Arc<std::sync::RwLock<Option<RtcpQualityHook>>>;
643
644fn current_quality_hook(slot: &QualityHookSlot) -> Option<RtcpQualityHook> {
645    match slot.read() {
646        Ok(held) => held.clone(),
647        Err(poisoned) => poisoned.into_inner().clone(),
648    }
649}
650
651fn replace_quality_hook(slot: &QualityHookSlot, hook: Option<RtcpQualityHook>) {
652    match slot.write() {
653        Ok(mut held) => *held = hook,
654        Err(poisoned) => *poisoned.into_inner() = hook,
655    }
656}
657
658/// A running media session.
659#[derive(Debug)]
660pub struct MediaSession {
661    /// The bound RTP socket, retained so an SDP renegotiation can rebuild codec workers without
662    /// rebinding the address the peer already knows.
663    socket: Arc<UdpSocket>,
664    /// The paired RTCP socket, for the same purpose.
665    rtcp_socket: Option<Arc<UdpSocket>>,
666    outgoing: mpsc::Sender<Frame>,
667    digits: Mutex<mpsc::Receiver<(Digit, Duration)>>,
668    /// Distinguishes one keypress from the next.
669    tones: AtomicU64,
670    incoming: Mutex<mpsc::Receiver<Vec<i16>>>,
671    encoded: Mutex<mpsc::Receiver<Encoded>>,
672    /// Whether received packets are handed on encoded rather than decoded to samples.
673    relay: Arc<AtomicBool>,
674    /// Whether this side's outbound audio is gated to silence (`M-18`).
675    muted: Arc<AtomicBool>,
676    /// Clips waiting to be played, in the order they were started (`M-17`).
677    clips: mpsc::Sender<Clip>,
678    /// Names the next playback. Never reused within a session.
679    playbacks: AtomicU64,
680    /// How many started clips have not yet resolved, for [`Self::flush`].
681    outstanding: Arc<AtomicUsize>,
682    /// A counter of full keypresses received, bumped by the receive loop after the digit is on
683    /// its way to the application. What an [`Interrupt::OnDigit`] playback watches.
684    keypresses: Arc<watch::Sender<u64>>,
685    codec: Codec,
686    wire_payload_type: u8,
687    receive_payload_type: u8,
688    /// Retained non-secret wire facts for validated runtime attachment after a host restart.
689    dtmf_payload_type: Option<u8>,
690    rtcp_mode: sipx_sdp::RtcpMode,
691    encrypted: bool,
692    local_addr: SocketAddr,
693    samples_per_packet: usize,
694    packet_duration: Duration,
695    clock_rate: u32,
696    /// The local SSRC carried by both RTP and RTCP for this generation.
697    ssrc: u32,
698    /// Application observation follows the logical session across worker replacement.
699    quality_hook: QualityHookSlot,
700    sent: Arc<AtomicU64>,
701    received: Arc<AtomicU64>,
702    /// Losses owned by this session, including candidate gathering on the port it consumed.
703    discards: Arc<DiscardMeters>,
704    stats: Arc<Mutex<StreamStats>>,
705    /// What the far end last told us, and when.
706    feedback: Arc<Mutex<Feedback>>,
707    /// The running ICE driver, for the exchanges that come after the one that started the session
708    /// ([`Self::renegotiate_ice`], `docs/specs/ice.md` §13.5).
709    ///
710    /// The loops were handed their own clones when they were spawned; this one is the signalling
711    /// layer's, and it is what makes a re-offer able to reach the agent at all. `None` is a stream
712    /// with no ICE, which is the default and must stay indistinguishable from the pre-ICE session.
713    ice: Option<crate::ice::driver::Handle>,
714    /// Browser-component security facts, when the one-owner path established this session.
715    browser_ingress: Option<Arc<std::sync::Mutex<crate::browser::ComponentIngress>>>,
716    /// Every asynchronous worker this session started. A handle remains registered while
717    /// `shutdown` awaits it, which makes a cancelled shutdown retryable instead of detached.
718    owners: Mutex<Vec<tokio::task::JoinHandle<()>>>,
719    /// Stopped generations replaced in place but not yet completely joined. The replacement owns
720    /// them before its first await, so cancellation cannot turn reconfiguration into detachment.
721    retired: Mutex<Vec<MediaSession>>,
722    #[cfg(all(test, feature = "dtls"))]
723    browser_profile_tasks: Option<Arc<crate::browser::ProfileTasks>>,
724    #[cfg(all(test, feature = "dtls"))]
725    browser_preparing_peak: Option<usize>,
726    stop: Arc<Stop>,
727}
728
729/// A sole-consumer linear-PCM view of one session's received audio.
730///
731/// The handle owns its rate-conversion history, so consecutive RTP frames remain one continuous
732/// output stream. Creating one does not spawn work; it reads the same bounded receive queue as
733/// [`MediaSession::recv`].
734#[derive(Debug)]
735pub struct PcmCapture<'a> {
736    session: &'a MediaSession,
737    format: sipx_audio::PcmFormat,
738    resampler: sipx_audio::LinearResampler,
739}
740
741impl PcmCapture<'_> {
742    /// The application format this capture emits.
743    #[must_use]
744    pub const fn format(&self) -> sipx_audio::PcmFormat {
745        self.format
746    }
747
748    /// Take the next non-empty converted PCM chunk.
749    pub async fn recv(&mut self) -> Option<sipx_audio::Pcm> {
750        loop {
751            let frame = self.session.recv().await?;
752            let converted = self.resampler.push_i16(&frame);
753            if !converted.is_empty() {
754                return Some(sipx_audio::Pcm::from_i16(self.format, converted));
755            }
756        }
757    }
758
759    /// Record at least `samples` in the chosen output format, or until `within` elapses.
760    ///
761    /// `within` bounds failure rather than defining stream silence, matching
762    /// [`MediaSession::record_at_least`]. Whatever arrived before the bound is retained.
763    pub async fn record_at_least(&mut self, samples: usize, within: Duration) -> sipx_audio::Pcm {
764        let deadline = tokio::time::Instant::now() + within;
765        let mut recorded = Vec::with_capacity(samples);
766        while recorded.len() < samples {
767            match tokio::time::timeout_at(deadline, self.session.recv()).await {
768                Ok(Some(frame)) => recorded.extend(self.resampler.push_i16(&frame)),
769                Ok(None) | Err(_) => break,
770            }
771        }
772        recorded.truncate(samples);
773        sipx_audio::Pcm::from_i16(self.format, recorded)
774    }
775}
776
777/// What this side has sent, as a sender report describes it (RFC 3550 §6.4.1).
778#[derive(Debug, Default)]
779struct Outbound {
780    packets: AtomicU64,
781    octets: AtomicU64,
782    /// The timestamp of the most recent packet, so a report can relate the RTP clock to the
783    /// wallclock — which is what lets a receiver synchronise two streams.
784    timestamp: std::sync::atomic::AtomicU32,
785}
786
787/// What the far end has told us, from the RTCP it sends back.
788#[derive(Debug, Default, Clone, Copy)]
789struct Feedback {
790    /// The most recent round-trip time, computed per RFC 3550 §6.4.1.
791    ///
792    /// Most recent rather than averaged: the calculation is a difference of two clocks, and a
793    /// clock that steps mid-call poisons an average for the rest of the session while it
794    /// only spoils one sample.
795    round_trip: Option<Duration>,
796    /// The middle 32 bits of the last sender report the far end sent us, and when it arrived.
797    /// Echoed back in our own reports so the far end can measure the round trip too.
798    last_sender_report: u32,
799    received_at: Option<tokio::time::Instant>,
800}
801
802/// Application-visible state updated by either RTCP receive shape.
803#[derive(Clone)]
804struct RtcpObservation {
805    feedback: Arc<Mutex<Feedback>>,
806    quality_hook: QualityHookSlot,
807    clock_rate: u32,
808}
809
810/// A stop signal: for a session's tasks, and — the same shape, one scope down — for one
811/// playback (`M-17`).
812///
813/// A flag *and* a notify. `Notify::notify_waiters` only wakes tasks already parked on it, so a
814/// loop that happens to be blocked on its channel when stop is called would never learn — and
815/// would go on sending audio into a call that had been hung up. The flag makes the signal
816/// durable; the notify makes it prompt.
817#[derive(Debug, Default)]
818pub(crate) struct Stop {
819    stopped: AtomicBool,
820    notify: tokio::sync::Notify,
821}
822
823impl Stop {
824    pub(crate) fn stop(&self) {
825        self.stopped.store(true, Ordering::SeqCst);
826        self.notify.notify_waiters();
827    }
828
829    pub(crate) fn is_stopped(&self) -> bool {
830        self.stopped.load(Ordering::SeqCst)
831    }
832
833    pub(crate) async fn wait(&self) {
834        // Register before reading the durable flag. The opposite order has a lost-wake window:
835        // `stop` can set the flag and notify after the check but before `notified()` registers,
836        // leaving this future asleep forever despite the flag saying it is stopped.
837        let notified = self.notify.notified();
838        tokio::pin!(notified);
839        notified.as_mut().enable();
840        if !self.is_stopped() {
841            notified.await;
842        }
843    }
844}
845
846/// The state every loop in a session shares, built in one place.
847///
848/// One place because two of these must not be rolled twice: RFC 3550 §8.1 requires a
849/// participant's RTCP to carry the same SSRC as its RTP, so the send loop and the report loop
850/// cannot each choose one, and §6.5.1's CNAME has to be stable for the session.
851struct Shared {
852    sent: Arc<AtomicU64>,
853    received: Arc<AtomicU64>,
854    discards: Arc<DiscardMeters>,
855    outbound: Arc<Outbound>,
856    feedback: Arc<Mutex<Feedback>>,
857    /// Zero until the first packet names the far end's synchronisation source.
858    stats: Arc<Mutex<StreamStats>>,
859    stop: Arc<Stop>,
860    ssrc: u32,
861    cname: String,
862    /// Whether received packets are handed on encoded rather than decoded to samples.
863    relay: Arc<AtomicBool>,
864    /// Whether this side's outbound audio is gated to silence (`M-18`).
865    muted: Arc<AtomicBool>,
866    /// A counter of full keypresses received, for an `Interrupt::OnDigit` playback to watch.
867    keypresses: Arc<watch::Sender<u64>>,
868    quality_hook: QualityHookSlot,
869}
870
871impl Shared {
872    fn new(local_addr: SocketAddr, discards: Arc<DiscardMeters>) -> Self {
873        Self::with_stop(local_addr, discards, Arc::new(Stop::default()))
874    }
875
876    fn with_stop(local_addr: SocketAddr, discards: Arc<DiscardMeters>, stop: Arc<Stop>) -> Self {
877        Self {
878            sent: Arc::new(AtomicU64::new(0)),
879            received: Arc::new(AtomicU64::new(0)),
880            discards,
881            outbound: Arc::new(Outbound::default()),
882            feedback: Arc::new(Mutex::new(Feedback::default())),
883            stats: Arc::new(Mutex::new(StreamStats::new(0))),
884            stop,
885            ssrc: rand::random(),
886            // Unique in user@host form and stable for the session: a random token distinguishes
887            // sessions on this host, the local address distinguishes hosts, and neither needs a
888            // name lookup on the media path.
889            cname: format!("{:08x}@{}", rand::random::<u32>(), local_addr),
890            relay: Arc::new(AtomicBool::new(false)),
891            muted: Arc::new(AtomicBool::new(false)),
892            keypresses: Arc::new(watch::Sender::new(0u64)),
893            quality_hook: Arc::new(std::sync::RwLock::new(None)),
894        }
895    }
896
897    fn rtcp_observation(&self, clock_rate: u32) -> RtcpObservation {
898        RtcpObservation {
899            feedback: Arc::clone(&self.feedback),
900            quality_hook: Arc::clone(&self.quality_hook),
901            clock_rate,
902        }
903    }
904}
905
906/// Identifies one playback on one session.
907///
908/// Carried by [`Playback`] and by the completion event a call reports it through, so a caller
909/// that started several clips can tell which of them the report is about.
910#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
911pub struct PlaybackId(u64);
912
913impl std::fmt::Display for PlaybackId {
914    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
915        write!(f, "{}", self.0)
916    }
917}
918
919/// How a playback ended.
920///
921/// A caller needs to be able to tell these apart: "the announcement finished", "the application
922/// cut it off", "the caller pressed a key", "the call went away underneath it" and "it never
923/// played at all" lead to different next steps. [`Self::completed`] is the one-bit answer for
924/// callers that only need to know whether the clip ran out.
925#[derive(Debug, Clone, Copy, PartialEq, Eq)]
926#[non_exhaustive]
927pub enum PlaybackEnd {
928    /// The whole clip reached the send queue.
929    Completed,
930    /// [`Playback::stop`] cut it short.
931    Stopped,
932    /// A keypress from the far end cut it short — it was started [`Interrupt::OnDigit`] and the
933    /// far end pressed a key (RFC 4733). The keypress itself is still delivered to whoever is
934    /// reading [`MediaSession::recv_digit`]; interrupting consumes nothing.
935    Interrupted,
936    /// The session stopped, or the call ended, under a playback still running.
937    SessionEnded,
938    /// The playback queue was full ([`Playback::QUEUE_DEPTH`] clips already waiting), so nothing was
939    /// played at all.
940    Refused,
941}
942
943impl PlaybackEnd {
944    /// Whether the clip ran to its end, as opposed to being cut short by anything.
945    #[must_use]
946    pub fn completed(self) -> bool {
947        matches!(self, Self::Completed)
948    }
949}
950
951/// Whether a keypress from the far end cuts a playback short.
952///
953/// This is the switch under the application contract's `gather{prompt, interruptible}`
954/// (`docs/specs/app-contract.md` §6.2): the prompt of a gather is interruptible by definition,
955/// and a bare `play` is not unless it says so.
956#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
957pub enum Interrupt {
958    /// The clip plays to its end whatever the far end presses.
959    #[default]
960    Never,
961    /// The first full keypress (RFC 4733) received *after this clip reaches the head of the
962    /// queue* stops it.
963    ///
964    /// After, not before: a key pressed while an earlier clip was still playing belongs to that
965    /// clip, and letting it arm this one would have a single keypress skip a whole prompt
966    /// sequence.
967    OnDigit,
968}
969
970/// A playback in progress, or one that has already ended.
971///
972/// Returned by [`MediaSession::start_playback`] without waiting for the clip: the point of the
973/// handle is that the caller goes on to do something else — collect digits, watch for a hangup —
974/// while the audio plays, and can reach back to stop it.
975///
976/// Cloneable, and deliberately so: the handle is a control surface, not ownership of a resource.
977/// A call hands one clone to the application and keeps another to report the playback's end on
978/// its event stream.
979#[derive(Debug, Clone)]
980pub struct Playback {
981    id: PlaybackId,
982    stop: Arc<Stop>,
983    end: watch::Receiver<Option<PlaybackEnd>>,
984}
985
986impl Playback {
987    /// How many packets of a stopped playback may still reach the wire after it is cut.
988    ///
989    /// **Two**, at 20 ms each by default — a number rather than "promptly", because the whole
990    /// difference between playback that can be controlled and playback that cannot is whether an
991    /// application can say how long barge-in takes.
992    ///
993    /// Where it comes from: [`MediaSession::start_playback`] runs ahead of the wire, so when a
994    /// clip is stopped the send queue is generally holding its next few dozen packets. Those are
995    /// not sent. The send loop tests each frame's playback against this signal as it takes the
996    /// frame off the queue and discards a stopped one *without spending a packet interval on it*,
997    /// so the whole backlog drains inside one tick. What can still go out is the packet the send
998    /// loop had already committed to the socket when the signal was set, and — allowing for the
999    /// stop landing between taking a frame and sending it — the one after it.
1000    ///
1001    /// The same bound covers [`Interrupt::OnDigit`]: an interruption sets the same signal, one
1002    /// task hop after the keypress is delivered.
1003    pub const STOP_BOUND_PACKETS: u64 = 2;
1004
1005    /// How many clips may be waiting behind the one playing before further ones are refused.
1006    ///
1007    /// A bound rather than an unbounded queue because the caller of
1008    /// [`MediaSession::start_playback`] is not always a program somebody wrote by hand — under the
1009    /// application contract it is a remote app sending instructions, and a queue that grows
1010    /// without limit turns a buggy app into this process's memory problem. Deep enough that no
1011    /// prompt sequence a call actually has time for will reach it.
1012    pub const QUEUE_DEPTH: usize = 32;
1013
1014    /// Which playback this is.
1015    #[must_use]
1016    pub fn id(&self) -> PlaybackId {
1017        self.id
1018    }
1019
1020    /// Cut this playback short.
1021    ///
1022    /// Takes effect within [`Self::STOP_BOUND_PACKETS`] packets. Idempotent, and harmless on a
1023    /// playback that has already ended. Does not wait: [`Self::finished`] is how a caller learns
1024    /// it has landed.
1025    pub fn stop(&self) {
1026        self.stop.stop();
1027    }
1028
1029    /// Whether this playback has been asked to stop — by [`Self::stop`] or by a keypress.
1030    #[must_use]
1031    pub fn is_stopped(&self) -> bool {
1032        self.stop.is_stopped()
1033    }
1034
1035    /// How it ended, if it has, without waiting.
1036    #[must_use]
1037    pub fn end(&self) -> Option<PlaybackEnd> {
1038        *self.end.borrow()
1039    }
1040
1041    /// Wait for it to end, and stop it if the wait itself is abandoned.
1042    ///
1043    /// The difference from [`Self::finished`] is what happens when *this future* is dropped
1044    /// before the clip ends — a caller that wrapped the wait in a `timeout`, or lost a `select!`.
1045    /// It stops the playback, because that is what abandoning a `play` has always meant: the
1046    /// audio stops with the caller's interest in it, rather than playing on out of a task the
1047    /// caller no longer holds a handle to.
1048    ///
1049    /// [`MediaSession::play`] is this method, which is how it keeps that property now that the
1050    /// clip is fed by a task of its own rather than by the caller.
1051    pub async fn play_out(&self) -> PlaybackEnd {
1052        /// Stops the playback if the wait is dropped before it settles. Not on the way out of a
1053        /// clip that ended on its own: the last packets of a completed clip are still in the send
1054        /// queue, and stopping then would discard them and clip the tail off every announcement.
1055        struct StopIfAbandoned<'a>(&'a Playback);
1056        impl Drop for StopIfAbandoned<'_> {
1057            fn drop(&mut self) {
1058                if self.0.end().is_none() {
1059                    self.0.stop();
1060                }
1061            }
1062        }
1063
1064        let guard = StopIfAbandoned(self);
1065        guard.0.finished().await
1066    }
1067
1068    /// Wait for it to end, and say how. Observation only: dropping this wait does not touch the
1069    /// playback, which goes on to whatever end it was going to reach.
1070    ///
1071    /// Resolves when the decision is made rather than when the last packet is on the wire — which
1072    /// is what a caller wants of an interruption, since the next thing it does is act on the
1073    /// keypress. The stopped clip's remaining audio is already guaranteed not to be sent by then.
1074    ///
1075    /// Takes `&self`, so several parties may await the same playback.
1076    pub async fn finished(&self) -> PlaybackEnd {
1077        let mut end = self.end.clone();
1078        loop {
1079            let settled = *end.borrow_and_update();
1080            if let Some(settled) = settled {
1081                return settled;
1082            }
1083            if end.changed().await.is_err() {
1084                // The queue task is gone without having recorded an end, which only happens when
1085                // the session went away underneath this clip.
1086                return PlaybackEnd::SessionEnded;
1087            }
1088        }
1089    }
1090}
1091
1092/// One clip on its way to the send queue, as the playback task receives it.
1093#[derive(Debug)]
1094struct Clip {
1095    samples: Vec<i16>,
1096    samples_per_packet: usize,
1097    interrupt: Interrupt,
1098    /// Shared with every [`Frame::Audio`] this clip produces, so stopping the playback also
1099    /// discards whatever of it the send queue is already holding.
1100    stop: Arc<Stop>,
1101    end: watch::Sender<Option<PlaybackEnd>>,
1102    /// How many full keypresses the receive loop has delivered. Watched, not consumed: an
1103    /// interruption must not take the digit away from the application.
1104    keypresses: watch::Receiver<u64>,
1105    /// How many clips the session has accepted and not yet resolved, so
1106    /// [`MediaSession::flush`] can tell a queue with work left in it from an empty one.
1107    /// Decremented by this clip's destructor, so it balances whether the clip played, was
1108    /// refused, or was dropped with the session.
1109    outstanding: Arc<AtomicUsize>,
1110    discards: Arc<DiscardMeters>,
1111}
1112
1113impl Clip {
1114    /// Record how this clip ended, for whoever is holding its [`Playback`].
1115    fn finish(&self, end: PlaybackEnd) {
1116        // Failure means every handle has been dropped, which is a caller that started a clip and
1117        // never looked back — a legitimate thing to do with an announcement.
1118        if self.end.send(Some(end)).is_err() {
1119            self.discards
1120                .playback_completion_unobserved
1121                .fetch_add(1, Ordering::Relaxed);
1122        }
1123    }
1124}
1125
1126impl Drop for Clip {
1127    fn drop(&mut self) {
1128        self.outstanding.fetch_sub(1, Ordering::SeqCst);
1129    }
1130}
1131
1132/// A bound media port that is not yet carrying anything.
1133///
1134/// This exists because of an ordering constraint in offer/answer: an SDP offer has to name the
1135/// port audio will arrive on, but the codec and the far end's address are not known until the
1136/// answer comes back. So the socket is bound first, its port goes into the offer, and the
1137/// session starts once there is something to start it with.
1138///
1139/// Binding twice instead — once to learn the port, once to start — fails with "address already
1140/// in use", which is how this type came to exist.
1141#[derive(Debug)]
1142pub struct MediaPort {
1143    socket: Arc<UdpSocket>,
1144    /// The control port, one above the media one (RFC 3550 §11).
1145    ///
1146    /// `None` when it could not be had. Media still works without it; what is lost is
1147    /// everything the far end would have told us about what it is hearing — including the
1148    /// round-trip time, which has nowhere else to come from.
1149    rtcp: Option<Arc<UdpSocket>>,
1150    local_addr: SocketAddr,
1151    /// Created at bind time so gathering losses survive the transition into a session.
1152    discards: Arc<DiscardMeters>,
1153}
1154
1155impl MediaPort {
1156    /// Bind a port, and the control port above it. Port 0 asks the OS to choose.
1157    ///
1158    /// RFC 3550 §11: RTP on an even port, RTCP on the next one up. They are bound together
1159    /// because a session that sends reports and cannot receive them is half a control
1160    /// protocol — it can tell the far end what it is hearing and can never learn what the far
1161    /// end hears, and the round-trip time comes from exactly that.
1162    ///
1163    /// Failing to get the control port is not failing to place the call. The pair is attempted,
1164    /// and if no pair is free the media port is taken alone and reporting is one-way.
1165    pub async fn bind(bind: SocketAddr) -> std::io::Result<Self> {
1166        const ATTEMPTS: usize = 16;
1167
1168        if bind.port() == 0 {
1169            for _ in 0..ATTEMPTS {
1170                let socket = UdpSocket::bind(bind).await?;
1171                let local_addr = socket.local_addr()?;
1172                // An odd media port has no room for its control port above it by convention.
1173                // Dropping the socket lets the OS hand the number out again.
1174                if local_addr.port() % 2 != 0 {
1175                    continue;
1176                }
1177                if let Some(rtcp) = bind_control_port(local_addr).await {
1178                    return Ok(Self {
1179                        socket: Arc::new(socket),
1180                        rtcp: Some(rtcp),
1181                        local_addr,
1182                        discards: Arc::new(DiscardMeters::default()),
1183                    });
1184                }
1185            }
1186        }
1187
1188        let socket = Arc::new(UdpSocket::bind(bind).await?);
1189        let local_addr = socket.local_addr()?;
1190        let rtcp = bind_control_port(local_addr).await;
1191        if rtcp.is_none() {
1192            tracing::debug!(%local_addr, "no control port; RTCP will be send-only");
1193        }
1194        Ok(Self {
1195            socket,
1196            rtcp,
1197            local_addr,
1198            discards: Arc::new(DiscardMeters::default()),
1199        })
1200    }
1201
1202    /// The port audio will arrive on — what goes in the SDP.
1203    #[must_use]
1204    pub fn local_addr(&self) -> SocketAddr {
1205        self.local_addr
1206    }
1207
1208    /// Whether this port got the control port above the media one (RFC 3550 §11).
1209    ///
1210    /// It decides what ICE may offer: `docs/specs/ice.md` §6.1 puts component 2 in the offer
1211    /// **only** when the control port was actually obtained, because a candidate for a socket
1212    /// that was never bound is an address the peer will check and nothing will answer on.
1213    #[must_use]
1214    pub fn has_control_port(&self) -> bool {
1215        self.rtcp.is_some()
1216    }
1217
1218    /// Run DTLS on this port and return it with the derived SRTP master material.
1219    ///
1220    /// The handshake borrows a duplicated descriptor for the same bound socket. No RTP worker is
1221    /// running yet, so it is the only reader; once it finishes, that duplicate is dropped and the
1222    /// original descriptor is restored to Tokio for [`Self::start`]. The timeout is enforced by
1223    /// the DTLS socket itself, making the blocking worker bounded even if this future is cancelled.
1224    #[cfg(feature = "dtls")]
1225    pub async fn key_with_dtls(
1226        self,
1227        identity: crate::dtls::openssl::Identity,
1228        peer: SocketAddr,
1229        role: crate::dtls::Role,
1230        fingerprint: sipx_sdp::fingerprint::Fingerprint,
1231        timeout: Duration,
1232    ) -> Result<(Self, SrtpKeys), DtlsStartError> {
1233        let Self {
1234            socket,
1235            rtcp,
1236            local_addr,
1237            discards,
1238        } = self;
1239        let socket = Arc::try_unwrap(socket).map_err(|_| DtlsStartError::SocketShared)?;
1240        let socket = socket.into_std()?;
1241        socket.set_nonblocking(false)?;
1242        let handshake_socket = socket.try_clone()?;
1243
1244        let keys = tokio::task::spawn_blocking(move || {
1245            let mut handshake =
1246                crate::dtls::openssl::Session::new(handshake_socket, peer, &identity, timeout)
1247                    .map_err(|error| crate::dtls::Error::Dtls(error.to_string()))?;
1248            crate::dtls::establish(&mut handshake, role, Some(&fingerprint))
1249        })
1250        .await
1251        .map_err(|error| DtlsStartError::Worker(error.to_string()))??
1252        .into_srtp_keys();
1253
1254        socket.set_read_timeout(None)?;
1255        socket.set_write_timeout(None)?;
1256        socket.set_nonblocking(true)?;
1257        let socket = UdpSocket::from_std(socket)?;
1258        Ok((
1259            Self {
1260                socket: Arc::new(socket),
1261                rtcp,
1262                local_addr,
1263                discards,
1264            },
1265            keys,
1266        ))
1267    }
1268
1269    /// Gather ICE candidates on this port's sockets (RFC 8445 §5.1.1).
1270    ///
1271    /// Called between binding and offering: the sockets are exclusively ours until
1272    /// [`Self::start`] or [`Self::start_with_ice`] spawns the loops that read them, which is the
1273    /// window a STUN transaction to a configured server needs.
1274    ///
1275    /// The result carries the `a=candidate` lines for the description
1276    /// ([`ice::LocalDescription::attributes`]) and the agent that will drive them.
1277    pub async fn gather(&self, gathering: &ice::Gathering) -> ice::LocalDescription {
1278        self.gather_with_rtcp_mode(gathering, sipx_sdp::RtcpMode::Separate)
1279            .await
1280    }
1281
1282    /// Gather ICE candidates for the negotiated RTCP shape.
1283    ///
1284    /// A muxed stream has only component 1. The default [`Self::gather`] retains the historical
1285    /// two-component behavior for callers that have not selected RFC 5761.
1286    pub async fn gather_with_rtcp_mode(
1287        &self,
1288        gathering: &ice::Gathering,
1289        rtcp_mode: sipx_sdp::RtcpMode,
1290    ) -> ice::LocalDescription {
1291        let mut bases = vec![ice::gather::Base {
1292            index: ice::LocalBase(0),
1293            component: ComponentId::RTP,
1294            socket: &self.socket,
1295        }];
1296        // Component 2 only when the control port was actually obtained (`ice.md` §6.1).
1297        if rtcp_mode == sipx_sdp::RtcpMode::Separate
1298            && let Some(rtcp) = &self.rtcp
1299        {
1300            bases.push(ice::gather::Base {
1301                index: ice::LocalBase(1),
1302                component: ComponentId::RTCP,
1303                socket: rtcp,
1304            });
1305        }
1306        ice::gather::gather(&bases, gathering, Arc::clone(&self.discards)).await
1307    }
1308
1309    /// Start carrying media, now that negotiation has said where and in what.
1310    ///
1311    /// Validation and construction finish before the first worker is spawned. On error this
1312    /// consumes and releases the bound port.
1313    ///
1314    /// # Errors
1315    ///
1316    /// Returns [`SetupError`] when timing is invalid or the negotiated codec cannot be built.
1317    pub fn start(self, config: Config) -> Result<MediaSession, SetupError> {
1318        let prepared = Prepared::new(&config)?;
1319        Ok(MediaSession::on_socket(
1320            &self.socket,
1321            self.rtcp,
1322            self.local_addr,
1323            config,
1324            None,
1325            prepared,
1326            self.discards,
1327        ))
1328    }
1329
1330    /// Start carrying media with ICE driving the path (`docs/specs/ice.md` §2, §11).
1331    ///
1332    /// The `local` description must already have been given the peer's half through
1333    /// [`ice::LocalDescription::accept`]. If that returned `false` — the peer offered no candidates,
1334    /// or RFC 8839 §5.3's `ice-mismatch` applies — no agent is driven and this is
1335    /// [`Self::start`]: no check is sent, no timer runs, and the stream is carried by symmetric
1336    /// RTP exactly as it is today.
1337    ///
1338    /// # Errors
1339    ///
1340    /// Returns [`SetupError`] before starting ICE or media workers when session construction is
1341    /// invalid.
1342    pub fn start_with_ice(
1343        self,
1344        config: Config,
1345        local: ice::LocalDescription,
1346    ) -> Result<MediaSession, SetupError> {
1347        let prepared = Prepared::new(&config)?;
1348        if !local.running() {
1349            return Ok(MediaSession::on_socket(
1350                &self.socket,
1351                self.rtcp,
1352                self.local_addr,
1353                config,
1354                None,
1355                prepared,
1356                self.discards,
1357            ));
1358        }
1359        Ok(MediaSession::on_socket(
1360            &self.socket,
1361            self.rtcp,
1362            self.local_addr,
1363            config,
1364            Some(local),
1365            prepared,
1366            self.discards,
1367        ))
1368    }
1369
1370    /// Start the fail-closed browser-audio runtime on this already-bound component.
1371    ///
1372    /// ICE begins first on the retained socket. Its selected component becomes the only DTLS
1373    /// peer; DTLS records pass through the component owner rather than a duplicated descriptor.
1374    /// Only a verified handshake installs SRTP/SRTCP keys and attaches the media workers.
1375    #[cfg(feature = "dtls")]
1376    #[allow(clippy::too_many_arguments)]
1377    pub async fn start_browser_audio(
1378        self,
1379        mut config: Config,
1380        local_ice: ice::LocalDescription,
1381        ice_generation: u64,
1382        identity: crate::dtls::openssl::Identity,
1383        role: crate::dtls::Role,
1384        peer_fingerprint: sipx_sdp::fingerprint::Fingerprint,
1385        timeout: Duration,
1386    ) -> Result<MediaSession, crate::browser::BrowserStartError> {
1387        if config.rtcp_mode != sipx_sdp::RtcpMode::Mux {
1388            return Err(crate::browser::BrowserStartError::RtcpMuxRequired);
1389        }
1390        let prepared = Prepared::new(&config)?;
1391        let stop = Arc::new(Stop::default());
1392        let (runtime, keys) = crate::browser::prepare(
1393            Arc::clone(&self.socket),
1394            local_ice,
1395            ice_generation,
1396            identity,
1397            role,
1398            peer_fingerprint,
1399            timeout,
1400            Arc::clone(&stop),
1401            Arc::clone(&self.discards),
1402        )
1403        .await?;
1404        let selected = crate::browser::lock_ingress(&runtime.ingress)
1405            .snapshot()
1406            .selected
1407            .ok_or(crate::browser::BrowserStartError::IceStopped)?;
1408        config.remote = selected.remote;
1409        config.srtp = Some(keys);
1410        Ok(MediaSession::on_browser(
1411            runtime,
1412            self.local_addr,
1413            config,
1414            prepared,
1415            self.discards,
1416        ))
1417    }
1418}
1419
1420impl MediaSession {
1421    /// Bind a socket and start the session in one step.
1422    ///
1423    /// Only for callers that already know the far end — an answerer, which has the offer in
1424    /// hand. A caller making the offer needs [`MediaPort`] instead.
1425    ///
1426    /// # Errors
1427    ///
1428    /// Returns [`StartError::Setup`] before binding for invalid timing or codec construction,
1429    /// and [`StartError::Io`] if the media sockets cannot be bound.
1430    pub async fn start(bind: SocketAddr, config: Config) -> Result<Self, StartError> {
1431        // Validation and stateful codec setup happen before binding, so a rejected setup never
1432        // occupies even a temporary port and can never leave a worker behind.
1433        let prepared = Prepared::new(&config)?;
1434        let port = MediaPort::bind(bind).await?;
1435        Ok(Self::on_socket(
1436            &port.socket,
1437            port.rtcp,
1438            port.local_addr,
1439            config,
1440            None,
1441            prepared,
1442            port.discards,
1443        ))
1444    }
1445
1446    // All fallible preparation precedes this constructor. Keeping worker creation contiguous is
1447    // what makes it reviewable that every worker shares the same sockets and stop token.
1448    #[allow(clippy::too_many_lines)]
1449    fn on_socket(
1450        socket: &Arc<UdpSocket>,
1451        rtcp: Option<Arc<UdpSocket>>,
1452        local_addr: SocketAddr,
1453        config: Config,
1454        ice: Option<ice::LocalDescription>,
1455        prepared: Prepared,
1456        discards: Arc<DiscardMeters>,
1457    ) -> Self {
1458        let samples_per_packet = config.samples_per_packet();
1459        let packet_duration = config.packet_duration;
1460        let clock_rate = config.clock_rate;
1461        let config_codec = config.codec;
1462        let wire_payload_type = config.wire_payload_type();
1463        let receive_payload_type = config.receive_wire_payload_type();
1464        let dtmf_payload_type = config.dtmf_payload_type;
1465        let rtcp_interval = config.rtcp_interval;
1466        let rtcp_mode = config.rtcp_mode;
1467        let encrypted = config.srtp.is_some();
1468        // A muxed session has exactly one running socket owner. The adjacent socket was reserved
1469        // before negotiation and is released now; no control worker may race the RTP reader.
1470        let rtcp = match rtcp_mode {
1471            sipx_sdp::RtcpMode::Separate => rtcp,
1472            sipx_sdp::RtcpMode::Mux => None,
1473        };
1474        let (outgoing_tx, outgoing_rx) = mpsc::channel::<Frame>(64);
1475        let (incoming_tx, incoming_rx) = mpsc::channel::<Vec<i16>>(256);
1476        let (encoded_tx, encoded_rx) = mpsc::channel::<Encoded>(256);
1477        let (digits_tx, digits_rx) = mpsc::channel::<(Digit, Duration)>(32);
1478
1479        let shared = Shared::new(local_addr, discards);
1480
1481        // Where to send. Starts at the SDP address and is replaced by the first observed
1482        // source: behind a NAT the advertised address is private and unreachable.
1483        let remote = Arc::new(Mutex::new(config.remote));
1484
1485        // Taken before `config` is moved into the receive loop. Both control loops need them,
1486        // and cloning a pair of keys is cheaper than cloning the whole configuration twice.
1487        let srtp_keys = config.srtp.clone();
1488
1489        // See `ice::driver::Destinations::rtcp`: `None` here, and for every stream without ICE,
1490        // leaves the report loop on RFC 3550 §11's convention.
1491        let rtcp_remote: Arc<Mutex<Option<SocketAddr>>> = Arc::new(Mutex::new(None));
1492        let (ice, ice_owner) = ice.map_or((None, None), |local| {
1493            let (handle, owner) = spawn_ice(
1494                local,
1495                socket,
1496                rtcp.as_ref(),
1497                &ice::driver::Destinations {
1498                    rtp: Arc::clone(&remote),
1499                    rtcp: Arc::clone(&rtcp_remote),
1500                },
1501                &shared.stop,
1502                &shared.discards,
1503            );
1504            (Some(handle), Some(owner))
1505        });
1506
1507        let send_owner = tokio::spawn(send_loop(
1508            Arc::clone(socket),
1509            outgoing_rx,
1510            Sending {
1511                remote: Arc::clone(&remote),
1512                config: config.clone(),
1513                ssrc: shared.ssrc,
1514                sent: Arc::clone(&shared.sent),
1515                outbound: Arc::clone(&shared.outbound),
1516                muted: Arc::clone(&shared.muted),
1517                ice: ice.clone(),
1518                stop: Arc::clone(&shared.stop),
1519                encoding: prepared.encoding,
1520                discards: Arc::clone(&shared.discards),
1521            },
1522        ));
1523        let (clips_tx, playback_owner) = spawn_playback_queue(&outgoing_tx, &shared.stop);
1524        let receive_owner = tokio::spawn(receive_loop(
1525            ReceiveInput::socket(Arc::clone(socket)),
1526            Inbound {
1527                audio: incoming_tx,
1528                encoded: encoded_tx,
1529                relay: Arc::clone(&shared.relay),
1530                digits: Keypresses {
1531                    to: digits_tx,
1532                    arrivals: Arc::clone(&shared.keypresses),
1533                },
1534                remote: Arc::clone(&remote),
1535                config,
1536                received: Arc::clone(&shared.received),
1537                stats: Arc::clone(&shared.stats),
1538                rtcp_observation: shared.rtcp_observation(clock_rate),
1539                ssrc: shared.ssrc,
1540                symmetric: ice.is_none(),
1541                ice: ice.clone(),
1542                browser_ingress: None,
1543                stop: Arc::clone(&shared.stop),
1544                decoding: prepared.decoding,
1545                discards: Arc::clone(&shared.discards),
1546            },
1547        ));
1548
1549        let rtcp_socket = rtcp.clone();
1550        let mut owners = vec![send_owner, playback_owner, receive_owner];
1551        owners.extend(spawn_control(Control {
1552            media: Arc::clone(socket),
1553            rtcp,
1554            remote: Arc::clone(&remote),
1555            rtcp_remote,
1556            interval: rtcp_interval,
1557            mode: rtcp_mode,
1558            ssrc: shared.ssrc,
1559            cname: shared.cname.clone(),
1560            stats: Arc::clone(&shared.stats),
1561            outbound: Arc::clone(&shared.outbound),
1562            rtcp_observation: shared.rtcp_observation(clock_rate),
1563            srtp: srtp_keys,
1564            ice: ice.clone(),
1565            stop: Arc::clone(&shared.stop),
1566            discards: Arc::clone(&shared.discards),
1567            #[cfg(feature = "dtls")]
1568            profile_tasks: None,
1569        }));
1570        if let Some(owner) = ice_owner {
1571            owners.push(owner);
1572        }
1573
1574        Self {
1575            socket: Arc::clone(socket),
1576            rtcp_socket,
1577            ice,
1578            outgoing: outgoing_tx,
1579            digits: Mutex::new(digits_rx),
1580            tones: AtomicU64::new(0),
1581            incoming: Mutex::new(incoming_rx),
1582            encoded: Mutex::new(encoded_rx),
1583            relay: shared.relay,
1584            muted: shared.muted,
1585            clips: clips_tx,
1586            playbacks: AtomicU64::new(0),
1587            outstanding: Arc::new(AtomicUsize::new(0)),
1588            keypresses: shared.keypresses,
1589            codec: config_codec,
1590            wire_payload_type,
1591            receive_payload_type,
1592            dtmf_payload_type,
1593            rtcp_mode,
1594            encrypted,
1595            local_addr,
1596            samples_per_packet,
1597            packet_duration,
1598            clock_rate,
1599            ssrc: shared.ssrc,
1600            quality_hook: shared.quality_hook,
1601            sent: shared.sent,
1602            received: shared.received,
1603            discards: shared.discards,
1604            stats: shared.stats,
1605            feedback: shared.feedback,
1606            browser_ingress: None,
1607            owners: Mutex::new(owners),
1608            retired: Mutex::new(Vec::new()),
1609            #[cfg(all(test, feature = "dtls"))]
1610            browser_profile_tasks: None,
1611            #[cfg(all(test, feature = "dtls"))]
1612            browser_preparing_peak: None,
1613            stop: shared.stop,
1614        }
1615    }
1616
1617    #[cfg(feature = "dtls")]
1618    #[allow(clippy::too_many_lines)]
1619    fn on_browser(
1620        runtime: crate::browser::Runtime,
1621        local_addr: SocketAddr,
1622        config: Config,
1623        prepared: Prepared,
1624        discards: Arc<DiscardMeters>,
1625    ) -> Self {
1626        let crate::browser::Runtime {
1627            socket,
1628            media,
1629            ice,
1630            ingress,
1631            owner,
1632            ice_owner,
1633            stop: runtime_stop,
1634            profile_tasks,
1635        } = runtime;
1636        let samples_per_packet = config.samples_per_packet();
1637        let packet_duration = config.packet_duration;
1638        let clock_rate = config.clock_rate;
1639        let config_codec = config.codec;
1640        let wire_payload_type = config.wire_payload_type();
1641        let receive_payload_type = config.receive_wire_payload_type();
1642        let dtmf_payload_type = config.dtmf_payload_type;
1643        let rtcp_mode = config.rtcp_mode;
1644        let encrypted = config.srtp.is_some();
1645        let rtcp_interval = config.rtcp_interval;
1646        let (outgoing_tx, outgoing_rx) = mpsc::channel::<Frame>(64);
1647        let (incoming_tx, incoming_rx) = mpsc::channel::<Vec<i16>>(256);
1648        let (encoded_tx, encoded_rx) = mpsc::channel::<Encoded>(256);
1649        let (digits_tx, digits_rx) = mpsc::channel::<(Digit, Duration)>(32);
1650        let shared = Shared::with_stop(local_addr, discards, runtime_stop);
1651        #[cfg(all(test, feature = "dtls"))]
1652        let preparing_peak = profile_tasks.counts().1;
1653        let remote = Arc::new(Mutex::new(config.remote));
1654        let srtp_keys = config.srtp.clone();
1655        let rtcp_remote: Arc<Mutex<Option<SocketAddr>>> = Arc::new(Mutex::new(None));
1656        let ice = Some(ice);
1657
1658        let send_owner = tokio::spawn(crate::browser::profile_task(
1659            Arc::clone(&profile_tasks),
1660            send_loop(
1661                Arc::clone(&socket),
1662                outgoing_rx,
1663                Sending {
1664                    remote: Arc::clone(&remote),
1665                    config: config.clone(),
1666                    ssrc: shared.ssrc,
1667                    sent: Arc::clone(&shared.sent),
1668                    outbound: Arc::clone(&shared.outbound),
1669                    muted: Arc::clone(&shared.muted),
1670                    ice: ice.clone(),
1671                    stop: Arc::clone(&shared.stop),
1672                    encoding: prepared.encoding,
1673                    discards: Arc::clone(&shared.discards),
1674                },
1675            ),
1676        ));
1677        let (clips_tx, playback_owner) =
1678            spawn_browser_playback_queue(&outgoing_tx, &shared.stop, Arc::clone(&profile_tasks));
1679        let receive_owner = tokio::spawn(crate::browser::profile_task(
1680            Arc::clone(&profile_tasks),
1681            receive_loop(
1682                ReceiveInput::Browser(media),
1683                Inbound {
1684                    audio: incoming_tx,
1685                    encoded: encoded_tx,
1686                    relay: Arc::clone(&shared.relay),
1687                    digits: Keypresses {
1688                        to: digits_tx,
1689                        arrivals: Arc::clone(&shared.keypresses),
1690                    },
1691                    remote: Arc::clone(&remote),
1692                    config,
1693                    received: Arc::clone(&shared.received),
1694                    stats: Arc::clone(&shared.stats),
1695                    rtcp_observation: shared.rtcp_observation(clock_rate),
1696                    ssrc: shared.ssrc,
1697                    symmetric: false,
1698                    ice: ice.clone(),
1699                    browser_ingress: Some(Arc::clone(&ingress)),
1700                    stop: Arc::clone(&shared.stop),
1701                    decoding: prepared.decoding,
1702                    discards: Arc::clone(&shared.discards),
1703                },
1704            ),
1705        ));
1706
1707        let mut owners = vec![owner, ice_owner, send_owner, playback_owner, receive_owner];
1708        owners.extend(spawn_control(Control {
1709            media: Arc::clone(&socket),
1710            rtcp: None,
1711            remote: Arc::clone(&remote),
1712            rtcp_remote,
1713            interval: rtcp_interval,
1714            mode: sipx_sdp::RtcpMode::Mux,
1715            ssrc: shared.ssrc,
1716            cname: shared.cname.clone(),
1717            stats: Arc::clone(&shared.stats),
1718            outbound: Arc::clone(&shared.outbound),
1719            rtcp_observation: shared.rtcp_observation(clock_rate),
1720            srtp: srtp_keys,
1721            ice: ice.clone(),
1722            stop: Arc::clone(&shared.stop),
1723            discards: Arc::clone(&shared.discards),
1724            #[cfg(feature = "dtls")]
1725            profile_tasks: Some(Arc::clone(&profile_tasks)),
1726        }));
1727
1728        Self {
1729            socket,
1730            rtcp_socket: None,
1731            ice,
1732            browser_ingress: Some(ingress),
1733            owners: Mutex::new(owners),
1734            retired: Mutex::new(Vec::new()),
1735            #[cfg(all(test, feature = "dtls"))]
1736            browser_profile_tasks: Some(profile_tasks),
1737            #[cfg(all(test, feature = "dtls"))]
1738            browser_preparing_peak: Some(preparing_peak),
1739            outgoing: outgoing_tx,
1740            digits: Mutex::new(digits_rx),
1741            tones: AtomicU64::new(0),
1742            incoming: Mutex::new(incoming_rx),
1743            encoded: Mutex::new(encoded_rx),
1744            relay: shared.relay,
1745            muted: shared.muted,
1746            clips: clips_tx,
1747            playbacks: AtomicU64::new(0),
1748            outstanding: Arc::new(AtomicUsize::new(0)),
1749            keypresses: shared.keypresses,
1750            codec: config_codec,
1751            wire_payload_type,
1752            receive_payload_type,
1753            dtmf_payload_type,
1754            rtcp_mode,
1755            encrypted,
1756            local_addr,
1757            samples_per_packet,
1758            packet_duration,
1759            clock_rate,
1760            ssrc: shared.ssrc,
1761            quality_hook: shared.quality_hook,
1762            sent: shared.sent,
1763            received: shared.received,
1764            discards: shared.discards,
1765            stats: shared.stats,
1766            feedback: shared.feedback,
1767            stop: shared.stop,
1768        }
1769    }
1770
1771    /// The address media arrives on, for the SDP.
1772    #[must_use]
1773    pub fn local_addr(&self) -> SocketAddr {
1774        self.local_addr
1775    }
1776
1777    /// This session generation's local RTP synchronisation source.
1778    #[must_use]
1779    pub fn local_ssrc(&self) -> u32 {
1780        self.ssrc
1781    }
1782
1783    /// Install or clear the application callback for peer RTCP quality reports.
1784    ///
1785    /// The slot is shared by this session's RTP/RTCP workers. Registering a callback does not
1786    /// enable RTCP when [`Config::rtcp_interval`] is `None`.
1787    pub fn set_rtcp_quality_hook(&self, hook: Option<RtcpQualityHook>) {
1788        replace_quality_hook(&self.quality_hook, hook);
1789    }
1790
1791    /// The callback currently installed for peer RTCP quality reports.
1792    #[must_use]
1793    pub fn rtcp_quality_hook(&self) -> Option<RtcpQualityHook> {
1794        current_quality_hook(&self.quality_hook)
1795    }
1796
1797    /// Whether ICE is driving this stream's path.
1798    ///
1799    /// The signalling layer's question before it builds any later description: RFC 8839 §4.4 makes
1800    /// the ICE attributes mandatory on every subsequent offer and answer for a stream doing ICE,
1801    /// and §6 makes their *absence* mean the peer has stopped. A session carrying no agent must
1802    /// therefore not grow ICE attributes on a re-offer, and one carrying an agent must not lose
1803    /// them.
1804    #[must_use]
1805    pub fn runs_ice(&self) -> bool {
1806        self.ice.is_some()
1807    }
1808
1809    /// The candidate path ICE actually selected for RTP.
1810    ///
1811    /// `Checking` is honest intermediate state: an ICE exchange was negotiated, but no nominated
1812    /// pair has replaced the default destination yet. A terminal diagnostic can therefore report
1813    /// what happened without inferring it from the policy that was requested.
1814    #[must_use]
1815    pub fn ice_path(&self) -> crate::ice::IcePath {
1816        self.ice.as_ref().map_or(
1817            crate::ice::IcePath::Disabled,
1818            crate::ice::driver::Handle::path,
1819        )
1820    }
1821
1822    /// Security and nominated-pair facts for a browser-audio component.
1823    #[must_use]
1824    pub fn browser_component(&self) -> Option<crate::browser::BrowserComponentSnapshot> {
1825        self.browser_ingress
1826            .as_ref()
1827            .map(|ingress| crate::browser::lock_ingress(ingress).snapshot())
1828    }
1829
1830    #[cfg(all(test, feature = "dtls"))]
1831    pub(crate) fn browser_task_counts(&self) -> Option<(usize, usize, usize)> {
1832        self.browser_profile_tasks.as_ref().and_then(|tasks| {
1833            self.browser_preparing_peak.map(|preparing_peak| {
1834                let (active, peak) = tasks.counts();
1835                (preparing_peak, active, peak)
1836            })
1837        })
1838    }
1839
1840    #[cfg(all(test, feature = "dtls"))]
1841    pub(crate) fn browser_task_probe(&self) -> Option<Arc<crate::browser::ProfileTasks>> {
1842        self.browser_profile_tasks.clone()
1843    }
1844
1845    /// Rebuild codec and packet workers on this session's existing sockets.
1846    ///
1847    /// Used when a later SDP exchange changes the remote address, codec, payload type, or keys.
1848    /// The local RTP/RTCP addresses do not change: they are already published to the peer, and a
1849    /// replacement that rebound an ephemeral port would make the new description false. Mute and
1850    /// encoded-relay policy survive the transition. The stopped generation remains owned until
1851    /// all of its workers have joined; if this future is cancelled during that join, the next
1852    /// reconfiguration or shutdown resumes the cleanup.
1853    ///
1854    /// Returns `false` without changing the session when ICE owns the destinations. Rebuilding an
1855    /// ICE-backed session requires the agent and its selected pair to move with the workers; a
1856    /// caller must refuse that renegotiation rather than silently fall back to an unchecked path.
1857    ///
1858    /// # Errors
1859    ///
1860    /// Returns [`SetupError`] before stopping the current workers if the new timing or codec
1861    /// cannot be constructed.
1862    pub async fn reconfigure(&mut self, config: Config) -> Result<bool, SetupError> {
1863        if self.ice.is_some() {
1864            return Ok(false);
1865        }
1866        let prepared = Prepared::new(&config)?;
1867        self.reap_retired().await;
1868        let muted = self.is_muted();
1869        let relay = self.relay.load(Ordering::SeqCst);
1870        let quality_hook = self.rtcp_quality_hook();
1871        let socket = Arc::clone(&self.socket);
1872        let rtcp = self.rtcp_socket.clone();
1873        let local_addr = self.local_addr;
1874
1875        self.stop.stop();
1876        let replacement = Self::on_socket(
1877            &socket,
1878            rtcp,
1879            local_addr,
1880            config,
1881            None,
1882            prepared,
1883            Arc::clone(&self.discards),
1884        );
1885        replacement.set_muted(muted);
1886        replacement.set_relay(relay);
1887        replacement.set_rtcp_quality_hook(quality_hook);
1888        let previous = std::mem::replace(self, replacement);
1889        self.retired.get_mut().push(previous);
1890        self.reap_retired().await;
1891        Ok(true)
1892    }
1893
1894    /// Apply a later exchange's ICE half, and read back what this side must now signal
1895    /// (RFC 8839 §4.4; `docs/specs/ice.md` §13.5).
1896    ///
1897    /// `local` carries fresh credentials and a fresh tiebreaker when this exchange is a restart —
1898    /// §4.4.1.1.1 says a new ICE session, and the answer to one names the answerer's *own* new
1899    /// credentials rather than the ones the finished session keyed its checks with. `None` is
1900    /// every ordinary re-offer: hold, resume, a codec change, a session refresh. `peer` is the
1901    /// description that arrived, when one has.
1902    ///
1903    /// Returns `None` when this stream is not running ICE, or when the driver has already stopped.
1904    /// Both mean the same thing to a caller: answer without ICE attributes rather than block on a
1905    /// session that is ending.
1906    ///
1907    /// Whether the peer's half *is* a restart is deliberately not asked here. That is
1908    /// §4.4.1.1.1's question about the peer's two credentials, the agent has always answered it,
1909    /// and a second implementation of it on this side would be a second thing to keep right.
1910    pub async fn renegotiate_ice(
1911        &self,
1912        local: Option<(sipx_sdp::ice::Credentials, u64)>,
1913        peer: Option<&ice::Negotiation>,
1914    ) -> Option<ice::Local> {
1915        let handle = self.ice.as_ref()?;
1916        let peer = match peer {
1917            Some(ice::Negotiation::Ice {
1918                credentials,
1919                candidates,
1920                lite,
1921            }) => Some(crate::ice::driver::Peer {
1922                credentials: credentials.clone(),
1923                candidates: candidates.clone(),
1924                lite: *lite,
1925            }),
1926            // `Absent` and `Mismatch` alike: RFC 8839 §5.3 says ICE MUST NOT be used for a
1927            // mismatched stream, and §6 says no candidates means no ICE. Neither is a description
1928            // to feed an agent — but this side still re-signals its own half, because the running
1929            // session is what the peer is sending media to.
1930            _ => None,
1931        };
1932        handle.renegotiated(local, peer).await
1933    }
1934
1935    /// Queue one packet's worth of samples.
1936    ///
1937    /// Queued rather than sent: the pacing timer decides when it goes out.
1938    pub async fn send(&self, samples: Vec<i16>) -> bool {
1939        self.outgoing
1940            .send(Frame::Audio {
1941                samples,
1942                playback: None,
1943            })
1944            .await
1945            .is_ok()
1946    }
1947
1948    /// Send a DTMF digit, held for `duration`.
1949    ///
1950    /// The packets go through the same paced queue as audio, so the tone occupies the slots
1951    /// audio would have. That is deliberate: RFC 4733 events replace the audio for their
1952    /// duration rather than being sent alongside it, and sending both means the far end hears
1953    /// the keypress twice.
1954    pub async fn send_digit(&self, digit: Digit, duration: Duration) -> bool {
1955        let per_packet = self.samples_per_packet;
1956        let packets = (duration.as_millis() / self.packet_duration.as_millis().max(1)).max(1);
1957        let events = dtmf::tone(
1958            digit,
1959            usize::try_from(packets).unwrap_or(1),
1960            u16::try_from(per_packet).unwrap_or(160),
1961        );
1962        let tone = self.tones.fetch_add(1, Ordering::Relaxed);
1963        for packet in events {
1964            if self
1965                .outgoing
1966                .send(Frame::Dtmf {
1967                    event: packet.event,
1968                    offset: packet.segment_offset,
1969                    tone,
1970                })
1971                .await
1972                .is_err()
1973            {
1974                return false;
1975            }
1976        }
1977        true
1978    }
1979
1980    /// Take the next DTMF digit the far end pressed, and how long it was held.
1981    ///
1982    /// The duration comes from the RFC 4733 event itself (its `duration` field, converted from
1983    /// the negotiated clock rate to wall-clock time), not from timing our own arrival: the event
1984    /// carries the sender's own clock, and measuring anything else would make the number depend
1985    /// on jitter rather than on how long the key was actually down.
1986    pub async fn recv_digit(&self) -> Option<(Digit, Duration)> {
1987        self.digits.lock().await.recv().await
1988    }
1989
1990    /// Collect the digits the far end presses, for at most `within`, stopping once it has been
1991    /// quiet for `gap`.
1992    ///
1993    /// Two questions, two bounds — the same split [`Self::record_at_least`] made on the audio
1994    /// path, and the reason this takes two durations rather than one.
1995    ///
1996    /// `within` bounds the wait for the **first** digit, and with it the whole collection. It is a
1997    /// **bound on failure**: how long this side is prepared to wait before concluding no digits
1998    /// are coming, so it belongs an order of magnitude above the honest answer — a whole call's
1999    /// worth, typically — rather than close to it. Nothing about it is a measurement: how long a
2000    /// caller takes to press the first key is a property of the caller, and how long the keypress
2001    /// takes to get here is a property of the machines between them.
2002    ///
2003    /// `gap` is a **definition of silence**: how long a caller has to leave a hole for the
2004    /// dialling to be treated as finished. It is the only question a fixed window can answer here,
2005    /// and it can only be asked once a digit has arrived, because a caller who has not dialled is
2006    /// not a caller who has stopped dialling.
2007    ///
2008    /// Whatever was collected is returned, including nothing. A collection cut short by `within`
2009    /// keeps the digits it already has.
2010    ///
2011    /// # Inferring the end of the dialling (`M-34`)
2012    ///
2013    /// RFC 4733 carries keypresses, not a completion signal: there is no "the caller is done"
2014    /// event to wait for, so *the digits ended* is always this side's inference from silence, and
2015    /// `gap` is the whole of that inference. What makes it safe to draw is that the input it draws
2016    /// on is exact rather than approximate. A digit is delivered here **once**, when the first
2017    /// packet carrying that tone's end bit arrives; the tone is identified by its own RTP
2018    /// timestamp, which is constant across every packet of the tone, so the end retransmissions
2019    /// RFC 4733 §2.5.1.3 asks for are absorbed rather than counted again, and "44" is told from a
2020    /// single long "4" by the timestamp changing. So a `gap` that elapses means no *keypress*
2021    /// completed in it — never that a packet was missed mid-tone.
2022    ///
2023    /// A digit that arrives a millisecond after `gap` expires is **not lost** — up to the 32 the
2024    /// keypress channel holds, past which the receive loop drops rather than blocks, deliberately
2025    /// and by the same reasoning as every other queue here. Within that bound it stays queued and
2026    /// is the first digit the next [`Self::recv_digit`] or `collect_digits` yields. It is in the
2027    /// wrong collection, though, and no wall clock can fix that — which is why `gap` is set past
2028    /// any plausible scheduling delay rather than close to the spacing digits actually arrive
2029    /// with, and why an application that knows how many digits it wants should stop at that count
2030    /// with [`Self::recv_digit`] instead of waiting for a silence at all.
2031    ///
2032    /// # Why this takes two durations (`M-34`)
2033    ///
2034    /// It used to take one, spent on both questions, and that is the defect `X-40` measured one
2035    /// layer up: a single window covering both "has it started" and "has it ended" is beaten by
2036    /// whichever of the two is slower on the day, and the result is not a degraded collection but
2037    /// an **empty** one, since the loop ends before its first iteration. `sipx answer` produced a
2038    /// valid recording of zero samples that way. Widening the single window would have moved that
2039    /// cliff rather than removed it, and left the same defect for a slower caller.
2040    pub async fn collect_digits(&self, within: Duration, gap: Duration) -> String {
2041        let deadline = tokio::time::Instant::now() + within;
2042        let mut out = String::new();
2043
2044        // The first digit. Nothing has been pressed yet, so there is no silence to interpret —
2045        // only the caller's own bound on how long to wait for dialling that may never start.
2046        match tokio::time::timeout_at(deadline, self.recv_digit()).await {
2047            Ok(Some((digit, _held))) => out.push(digit.as_char()),
2048            // The session ended, or the bound elapsed. Either way nobody dialled.
2049            Ok(None) | Err(_) => return out,
2050        }
2051
2052        // The rest of the sequence. A gap now does mean the dialling has finished, and `within`
2053        // still caps a far end that keeps pressing keys forever.
2054        loop {
2055            let next = tokio::time::Instant::now() + gap;
2056            match tokio::time::timeout_at(next.min(deadline), self.recv_digit()).await {
2057                Ok(Some((digit, _held))) => out.push(digit.as_char()),
2058                // The caller stopped, the session ended, or the collection's time is up. All
2059                // three mean this is every digit there is — and it is kept.
2060                Ok(None) | Err(_) => return out,
2061            }
2062        }
2063    }
2064
2065    /// The codec this session negotiated.
2066    #[must_use]
2067    pub fn codec(&self) -> Codec {
2068        self.codec
2069    }
2070
2071    /// The payload type this negotiated stream puts on the wire.
2072    ///
2073    /// Static codecs usually return their assigned number. Dynamic codecs return the number
2074    /// from the negotiated description, which need not be the number this endpoint prefers in
2075    /// an offer.
2076    #[must_use]
2077    pub fn wire_payload_type(&self) -> u8 {
2078        self.wire_payload_type
2079    }
2080
2081    /// The payload type this negotiated stream accepts from the wire.
2082    #[must_use]
2083    pub fn receive_payload_type(&self) -> u8 {
2084        self.receive_payload_type
2085    }
2086
2087    /// The negotiated RTP payload type for telephone events, when enabled.
2088    #[must_use]
2089    pub fn dtmf_payload_type(&self) -> Option<u8> {
2090        self.dtmf_payload_type
2091    }
2092
2093    /// Whether RTP and RTCP share one socket for this session.
2094    #[must_use]
2095    pub fn rtcp_mode(&self) -> sipx_sdp::RtcpMode {
2096        self.rtcp_mode
2097    }
2098
2099    /// Whether this session was constructed with SRTP key material.
2100    ///
2101    /// The key bytes remain owned by the workers and are never exposed by this fact.
2102    #[must_use]
2103    pub fn is_encrypted(&self) -> bool {
2104        self.encrypted
2105    }
2106
2107    /// The RTP timestamp clock negotiated for this stream.
2108    ///
2109    /// This is intentionally the wire clock rather than an inferred playback rate. In
2110    /// particular, RFC 7587 fixes Opus at 48 kHz on the RTP timeline.
2111    #[must_use]
2112    pub fn clock_rate(&self) -> u32 {
2113        self.clock_rate
2114    }
2115
2116    /// Hand received packets on still encoded, rather than decoding them to samples.
2117    ///
2118    /// Switchable at run time because a bridge is formed between calls that are already
2119    /// running: the decision belongs to whoever connects them, and it is not known when the
2120    /// session starts.
2121    pub fn set_relay(&self, relay: bool) {
2122        self.relay.store(relay, Ordering::SeqCst);
2123    }
2124
2125    /// Gate this session's outbound audio, or let it through again (`M-18`).
2126    ///
2127    /// Returns what the gate was set to before, so a caller that only wants to report real
2128    /// transitions does not have to read the flag and then write it — two steps that race each
2129    /// other when a call is muted from more than one place.
2130    ///
2131    /// **What muting does to the stream.** Every audio frame the send loop takes off the queue is
2132    /// replaced by the same number of samples of silence, encoded in this session's own codec and
2133    /// sent on its own payload type. The stream keeps its pacing, its sequence numbers and its
2134    /// timestamps; what changes is only what the far end decodes. See the module documentation
2135    /// for why this rather than suppressing the packets, and for the RFC 3550 §6 consequence.
2136    ///
2137    /// Reception is not affected in any way, and neither is DTMF: an RFC 4733 event is generated
2138    /// by this endpoint on purpose, the way a keypad tone is on a handset, so it goes out muted
2139    /// or not.
2140    pub fn set_muted(&self, muted: bool) -> bool {
2141        self.muted.swap(muted, Ordering::SeqCst)
2142    }
2143
2144    /// Whether this session's outbound audio is gated to silence.
2145    #[must_use]
2146    pub fn is_muted(&self) -> bool {
2147        self.muted.load(Ordering::SeqCst)
2148    }
2149
2150    /// Take the next packet as it arrived, still encoded. Only ever yields under
2151    /// [`Self::set_relay`].
2152    pub async fn recv_encoded(&self) -> Option<Encoded> {
2153        self.encoded.lock().await.recv().await
2154    }
2155
2156    /// Put a payload on the wire exactly as given, bypassing the codec.
2157    pub async fn send_encoded(&self, encoded: Encoded) -> bool {
2158        self.outgoing
2159            .send(Frame::Encoded {
2160                payload_type: encoded.payload_type,
2161                payload: encoded.payload,
2162            })
2163            .await
2164            .is_ok()
2165    }
2166
2167    /// Take the next packet's worth of received samples.
2168    pub async fn recv(&self) -> Option<Vec<i16>> {
2169        self.incoming.lock().await.recv().await
2170    }
2171
2172    /// Receive this session as linear PCM at an application-chosen rate and depth.
2173    ///
2174    /// # Errors
2175    ///
2176    /// Returns [`sipx_audio::PcmError::UnsupportedSampleRate`] when the requested format's rate
2177    /// cannot be converted safely.
2178    pub fn capture(
2179        &self,
2180        format: sipx_audio::PcmFormat,
2181    ) -> Result<PcmCapture<'_>, sipx_audio::PcmError> {
2182        Ok(PcmCapture {
2183            session: self,
2184            format,
2185            resampler: sipx_audio::LinearResampler::new(self.clock_rate, format.sample_rate())?,
2186        })
2187    }
2188
2189    /// Take received samples until the session goes quiet for `idle`.
2190    ///
2191    /// The idle timeout rather than a packet count, because the caller generally knows how long
2192    /// the far end will talk for and not how many packets that becomes.
2193    ///
2194    /// `idle` answers one question — *has the far end stopped talking* — and it answers it by
2195    /// wall clock. A caller that already knows how many samples it expects is asking a different
2196    /// question and wants [`Self::record_at_least`]; see there for what goes wrong when the two
2197    /// are confused (`X-28`).
2198    pub async fn record_until_idle(&self, idle: Duration) -> Vec<i16> {
2199        let mut samples = Vec::new();
2200        // `idle` is a definition of silence, and on the first pass it is also the deadline for the
2201        // stream to start — which is the trap the documentation above is about, kept deliberately
2202        // rather than hidden. The remedy is not a wider window here; it is [`Self::record_at_least`]
2203        // for every caller that knows the count, which is nearly all of them (`X-28`, `X-44`).
2204        while let Ok(Some(frame)) = tokio::time::timeout(idle, self.recv()).await {
2205            samples.extend_from_slice(&frame);
2206        }
2207        samples
2208    }
2209
2210    /// Take received samples until `samples` of them have arrived, or `within` elapses.
2211    ///
2212    /// The wait for a caller that knows the size of what the far end was given — a test that
2213    /// played a clip of its own, most often. `within` is a **bound on failure**, not a
2214    /// measurement: it is how long this side is prepared to wait before concluding the audio is
2215    /// not coming, so it should be far longer than the clip rather than close to it. Whatever
2216    /// arrived is returned, so a caller that got fewer samples than it asked for can say so
2217    /// itself.
2218    ///
2219    /// # Why this exists (`X-28`)
2220    ///
2221    /// [`Self::record_until_idle`] spends one duration on two different jobs: how long to wait
2222    /// for the stream to *start*, and how long a gap means it has *ended*. Neither is a property
2223    /// of the audio — both are properties of how fast the machine happens to be — so a caller
2224    /// that knows the count and uses the idle window instead is racing a fixed wall clock
2225    /// against a pipeline that is merely slow. On a loaded machine that pipeline is slow in
2226    /// exactly the two places the single window covers: the first packet is the one that waits
2227    /// out both jitter buffers filling, and a stalled scheduler opens mid-stream gaps wider than
2228    /// any packet interval. The observed result is a recording of **zero** samples — not a
2229    /// degraded one — because once the first frame lands the rest follow at the packet rate.
2230    ///
2231    /// Widening the window would not have fixed that; it would have moved the cliff.
2232    pub async fn record_at_least(&self, samples: usize, within: Duration) -> Vec<i16> {
2233        let deadline = tokio::time::Instant::now() + within;
2234        let mut recorded = Vec::with_capacity(samples);
2235        while recorded.len() < samples {
2236            match tokio::time::timeout_at(deadline, self.recv()).await {
2237                Ok(Some(frame)) => recorded.extend_from_slice(&frame),
2238                // The session ended, or the bound elapsed. Either way this is everything there
2239                // is, and it is short — which is the caller's to report, not this method's.
2240                Ok(None) | Err(_) => break,
2241            }
2242        }
2243        recorded
2244    }
2245
2246    /// How many samples one packet of this session's audio carries.
2247    ///
2248    /// Settled once when the session started, from the negotiated codec's clock rate and the
2249    /// packet duration. Exposed so a caller playing a clip does not have to recompute what the
2250    /// session already decided — and get it wrong for a codec whose rate is not 8 kHz.
2251    #[must_use]
2252    pub fn samples_per_packet(&self) -> usize {
2253        self.samples_per_packet
2254    }
2255
2256    /// Send a whole clip, paced by the send loop, and wait for it.
2257    ///
2258    /// Returns whether the clip reached the end. `false` means it did not: the send queue closed
2259    /// part way — the call ended, or the session was stopped, under a playback still running — or
2260    /// something cut it short. The caller needs to be able to tell those apart: "the clip
2261    /// finished" and "the clip was cut off" are different things to anything waiting on the
2262    /// playback, and returning `()` made them indistinguishable.
2263    ///
2264    /// This is [`Self::start_playback`] with the handle thrown away and the answer awaited
2265    /// through [`Playback::play_out`], so it stays cancel-on-drop: a caller that wraps it in a
2266    /// `timeout` still stops the audio when the timeout fires. A caller that wants to stop the
2267    /// clip explicitly, or to have a keypress stop it, needs the handle.
2268    pub async fn play(&self, samples: &[i16], samples_per_packet: usize) -> bool {
2269        self.start_clip(samples.to_vec(), samples_per_packet, Interrupt::Never)
2270            .play_out()
2271            .await
2272            .completed()
2273    }
2274
2275    /// Convert and play an explicit linear-PCM buffer.
2276    ///
2277    /// # Errors
2278    ///
2279    /// Returns [`sipx_audio::PcmError`] before queuing anything when its rate or representation
2280    /// cannot be converted.
2281    pub async fn play_pcm(&self, pcm: &sipx_audio::Pcm) -> Result<bool, sipx_audio::PcmError> {
2282        Ok(self
2283            .start_pcm_playback(pcm, Interrupt::Never)?
2284            .play_out()
2285            .await
2286            .completed())
2287    }
2288
2289    /// Convert an explicit PCM buffer and start it as a controllable playback.
2290    ///
2291    /// # Errors
2292    ///
2293    /// Returns [`sipx_audio::PcmError`] before creating a playback when conversion is refused.
2294    pub fn start_pcm_playback(
2295        &self,
2296        pcm: &sipx_audio::Pcm,
2297        interrupt: Interrupt,
2298    ) -> Result<Playback, sipx_audio::PcmError> {
2299        let samples = pcm.to_i16(self.clock_rate)?;
2300        Ok(self.start_playback(samples, interrupt))
2301    }
2302
2303    /// Start a clip and hand back a handle to it, without waiting (`M-17`).
2304    ///
2305    /// The clip is played at this session's own packet size, so it is right under a codec whose
2306    /// clock is not 8 kHz without the caller knowing the rate.
2307    ///
2308    /// # Clips queue; they do not replace
2309    ///
2310    /// Starting a second playback while one is running puts it **behind** the one playing, and it
2311    /// begins when that one ends — however that one ends. This is the choice the story left open,
2312    /// and it is recorded in [`docs/designs/app-sdk.md`](../../../docs/designs/app-sdk.md). The
2313    /// reasoning in short: replacement would make "stop" an implicit side effect of "play", so an
2314    /// application that wanted a prompt followed by a menu would hear only the menu, and the first
2315    /// clip's cancellation would be an event nobody asked for. Replacement is still available and
2316    /// still says what it means — [`Playback::stop`] the one playing, then start the next.
2317    ///
2318    /// Queueing while a clip is stopping is the case worth naming, because it is what barge-in
2319    /// does: stop the prompt, then immediately play something else. The clip being stopped
2320    /// releases the queue at once and its unsent packets are discarded rather than played, so the
2321    /// new clip starts within [`Playback::STOP_BOUND_PACKETS`] packets — it does not have to wait
2322    /// out the backlog of the clip it replaced.
2323    ///
2324    /// A queue [`Playback::QUEUE_DEPTH`] deep. A clip that arrives at a full queue is not played and its
2325    /// handle resolves immediately as [`PlaybackEnd::Refused`], rather than being silently
2326    /// dropped or waiting for room that a live call may never have.
2327    pub fn start_playback(&self, samples: Vec<i16>, interrupt: Interrupt) -> Playback {
2328        self.start_clip(samples, self.samples_per_packet, interrupt)
2329    }
2330
2331    /// Queue a clip at an explicit packet size.
2332    ///
2333    /// Separate from [`Self::start_playback`] only because [`Self::play`] takes the size from its
2334    /// caller and has done since before this session knew its own.
2335    fn start_clip(
2336        &self,
2337        samples: Vec<i16>,
2338        samples_per_packet: usize,
2339        interrupt: Interrupt,
2340    ) -> Playback {
2341        let id = PlaybackId(self.playbacks.fetch_add(1, Ordering::Relaxed));
2342        let stop = Arc::new(Stop::default());
2343        let (end_tx, end_rx) = watch::channel(None);
2344        let playback = Playback {
2345            id,
2346            stop: Arc::clone(&stop),
2347            end: end_rx,
2348        };
2349
2350        // Counted before the hand-off, so a `flush` racing this call never sees a queue it
2351        // believes is empty. The clip's destructor is what takes it back down again, on every
2352        // path out including the two below.
2353        self.outstanding.fetch_add(1, Ordering::SeqCst);
2354        let clip = Clip {
2355            samples,
2356            samples_per_packet: samples_per_packet.max(1),
2357            interrupt,
2358            stop,
2359            end: end_tx,
2360            keypresses: self.keypresses.subscribe(),
2361            outstanding: Arc::clone(&self.outstanding),
2362            discards: Arc::clone(&self.discards),
2363        };
2364
2365        // `try_send` rather than an await, so starting a playback is not itself something that
2366        // can park — a handle the caller cannot yet hold is a handle it cannot stop.
2367        match self.clips.try_send(clip) {
2368            Ok(()) => {}
2369            Err(mpsc::error::TrySendError::Full(clip)) => clip.finish(PlaybackEnd::Refused),
2370            Err(mpsc::error::TrySendError::Closed(clip)) => clip.finish(PlaybackEnd::SessionEnded),
2371        }
2372        playback
2373    }
2374
2375    /// How many packets have been sent.
2376    #[must_use]
2377    pub fn packets_sent(&self) -> u64 {
2378        self.sent.load(Ordering::Relaxed)
2379    }
2380
2381    /// How many have been received.
2382    #[must_use]
2383    pub fn packets_received(&self) -> u64 {
2384        self.received.load(Ordering::Relaxed)
2385    }
2386
2387    /// A synchronous snapshot of everything this session's media path has discarded.
2388    ///
2389    /// This includes candidate-gathering losses on the [`MediaPort`] the session consumed.
2390    /// Each field is monotonic, but independent workers can advance different fields while this
2391    /// snapshot is read, so relationships across fields are exact only while the session is quiet.
2392    #[must_use]
2393    pub fn discard_counts(&self) -> MediaDiscardCounts {
2394        self.discards.snapshot()
2395    }
2396
2397    /// How the call is going: loss, jitter, round-trip time and an estimated score.
2398    ///
2399    /// Readable at any point, not only at the end. The round-trip time is `None` until a report
2400    /// has come back from the far end carrying an echo of one of ours — which needs both a
2401    /// control port on this side and a peer that answers, so it stays `None` against a peer
2402    /// that does not do RTCP rather than being filled in with a guess.
2403    pub async fn quality(&self) -> sipx_rtp::Quality {
2404        // The counters directly, never `report_block()`: that one closes the reporting interval
2405        // (RFC 3550 §6.4.1), so an application polling quality every second would quietly empty
2406        // the window the next RTCP report was going to describe, and the far end would be told
2407        // the call was clean. `pending_report_block()` would be safe now, but the whole-call
2408        // figures below are not in a report block at all.
2409        let (lost, expected, jitter_units) = {
2410            let stats = self.stats.lock().await;
2411            (stats.cumulative_lost(), stats.expected(), stats.jitter())
2412        };
2413
2414        // Loss over the whole call, not since the last report. The per-report fraction is the
2415        // right number to *send* and the wrong one to *show*: it swings between 0 and 1 with
2416        // each interval, and an application sampling it sees whichever interval it happened to
2417        // catch rather than how the call has gone.
2418        // `f64` throughout: a packet count large enough to lose precision here is a call that
2419        // has been running for tens of thousands of years.
2420        let fraction = if expected > 0 {
2421            let (missing, due) = (
2422                u32::try_from(lost.max(0)).map_or(f64::from(u32::MAX), f64::from),
2423                u32::try_from(expected).map_or(f64::from(u32::MAX), f64::from),
2424            );
2425            missing / due
2426        } else {
2427            0.0
2428        };
2429        // The jitter field is in timestamp units; turning it into a duration needs the clock
2430        // rate, which is the one number that makes it comparable between codecs.
2431        let jitter = Duration::from_secs_f64(
2432            (f64::from(jitter_units) / f64::from(self.clock_rate)).max(0.0),
2433        );
2434        let round_trip = self.feedback.lock().await.round_trip;
2435
2436        sipx_rtp::Quality {
2437            loss: fraction,
2438            cumulative_lost: lost,
2439            jitter,
2440            round_trip,
2441            mos: sipx_rtp::Quality::mos(fraction, jitter, round_trip),
2442        }
2443    }
2444
2445    /// The receiver report this session would send right now (RFC 3550 §6.4.1).
2446    ///
2447    /// **Safe to poll**, as often as a dashboard likes: reading does not close the reporting
2448    /// interval, so it cannot make the *next* RTCP report claim a clean interval that was in fact
2449    /// lossy. That is a decision and not an accident (`M-33`) — §6.4.1 defines `fraction_lost` as
2450    /// loss since the previous SR or RR *packet*, so the interval boundary is a report having been
2451    /// sent, and a read is not one. The RTCP loop closes it, via
2452    /// [`StreamStats::report_block`](sipx_rtp::rtcp::StreamStats::report_block); this reads with
2453    /// [`pending_report_block`](sipx_rtp::rtcp::StreamStats::pending_report_block).
2454    ///
2455    /// `fraction_lost` is therefore whatever has accumulated since the last report went out, which
2456    /// makes it a poor thing to *display*: it swings with each interval, and a poller sees whichever
2457    /// interval it happened to catch. [`Self::quality`] is the figure for a caller to show.
2458    ///
2459    /// The two echo fields are zero here. They are filled in by the sending loop, which is the only
2460    /// place that knows how long a peer's sender report has been held.
2461    pub async fn stats(&self) -> sipx_rtp::rtcp::ReportBlock {
2462        self.stats.lock().await.pending_report_block()
2463    }
2464
2465    /// Wait until everything queued has actually been sent.
2466    ///
2467    /// Sending is paced, so `play` and `send_digit` return as soon as the packets are queued —
2468    /// which is long before they are on the wire. Hanging up at that point discards the tail:
2469    /// the last word of a clip, or the last digit of a PIN. Anything still queued after
2470    /// `within` is given up on, so this cannot hold a caller open indefinitely.
2471    pub async fn flush(&self, within: Duration) {
2472        let deadline = tokio::time::Instant::now() + within;
2473        // Both queues: a clip started but not yet fed to the send loop has nothing in the send
2474        // queue to see, and a flush that only looked there would hang up over the top of it.
2475        while self.outstanding.load(Ordering::SeqCst) > 0
2476            || self.outgoing.capacity() < self.outgoing.max_capacity()
2477        {
2478            if tokio::time::Instant::now() >= deadline || self.stop.is_stopped() {
2479                return;
2480            }
2481            tokio::time::sleep(self.packet_duration.max(Duration::from_millis(5))).await;
2482        }
2483        // The last packet has left the queue but not yet the socket.
2484        tokio::time::sleep(self.packet_duration).await;
2485    }
2486
2487    /// Stop the session and release its socket.
2488    pub fn stop(&self) {
2489        self.stop.stop();
2490    }
2491
2492    /// Stop and join every worker owned by this session.
2493    ///
2494    /// Handles stay in the registry until their await completes. Cancelling this future therefore
2495    /// leaves the current handle owned, and a later call resumes the same drain.
2496    pub async fn shutdown(&self) {
2497        self.stop.stop();
2498        if let Some(ingress) = &self.browser_ingress {
2499            crate::browser::lock_ingress(ingress).close();
2500        }
2501        let mut owners = self.owners.lock().await;
2502        while let Some(owner) = owners.last_mut() {
2503            // discard: every worker shares the observed stop token; awaiting proves it is reaped,
2504            // while a cancellation JoinError adds no packet-level discard to count.
2505            let _ = owner.await;
2506            owners.pop();
2507        }
2508        drop(owners);
2509        self.reap_retired().await;
2510    }
2511
2512    async fn reap_retired(&self) {
2513        let mut retired = self.retired.lock().await;
2514        while let Some(previous) = retired.last() {
2515            Box::pin(previous.shutdown()).await;
2516            retired.pop();
2517        }
2518    }
2519
2520    #[cfg(test)]
2521    async fn owned_task_count(&self) -> usize {
2522        self.owners.lock().await.len() + self.retired.lock().await.len()
2523    }
2524
2525    /// Whether the session has been stopped.
2526    #[must_use]
2527    pub fn is_stopped(&self) -> bool {
2528        self.stop.is_stopped()
2529    }
2530}
2531
2532impl Drop for MediaSession {
2533    fn drop(&mut self) {
2534        // A session that outlives its call keeps a socket and two tasks alive. On a server
2535        // taking calls all day that is the difference between steady and unbounded.
2536        self.stop.stop();
2537        if let Some(ingress) = &self.browser_ingress {
2538            crate::browser::lock_ingress(ingress).close();
2539        }
2540        for owner in self.owners.get_mut().drain(..) {
2541            owner.abort();
2542        }
2543    }
2544}
2545
2546/// Where a received packet goes, gathered so the receive loop reads as a loop.
2547fn delivery<'a>(
2548    audio: &'a mpsc::Sender<Vec<i16>>,
2549    encoded: &'a mpsc::Sender<Encoded>,
2550    relay: &'a AtomicBool,
2551    discards: &'a DiscardMeters,
2552) -> Delivery<'a> {
2553    Delivery {
2554        audio,
2555        encoded,
2556        relay,
2557        discards,
2558    }
2559}
2560
2561/// Release everything the jitter buffer is holding, because nothing more is coming.
2562#[allow(clippy::too_many_arguments)]
2563async fn flush(
2564    buffer: &mut JitterBuffer,
2565    to: &Delivery<'_>,
2566    decoding: &mut Decoding,
2567    digits: &Keypresses,
2568    dtmf: &mut sipx_rtp::dtmf::Receiver,
2569    config: &Config,
2570    stop: &Stop,
2571) -> bool {
2572    for packet in buffer.drain() {
2573        if !deliver(to, decoding, digits, dtmf, config, stop, &packet).await {
2574            return false;
2575        }
2576    }
2577    true
2578}
2579
2580/// Authenticate and decrypt a datagram, or drop it.
2581///
2582/// `None` means the packet does not belong to this stream and nothing about it should reach the
2583/// parser, the jitter buffer or the statistics — which is the point of authenticating at all:
2584/// a forged packet must not be able to move any state.
2585fn authenticated(
2586    context: Option<&mut sipx_rtp::SrtpContext>,
2587    bytes: Bytes,
2588    source: SocketAddr,
2589    discards: &DiscardMeters,
2590    browser_ingress: Option<&Arc<std::sync::Mutex<crate::browser::ComponentIngress>>>,
2591) -> Option<Bytes> {
2592    let Some(context) = context else {
2593        return Some(bytes);
2594    };
2595    match context.unprotect(&bytes) {
2596        Ok(plain) => Some(Bytes::from(plain)),
2597        Err(error) => {
2598            discards
2599                .srtp_unprotect_failures
2600                .fetch_add(1, Ordering::Relaxed);
2601            if let Some(ingress) = browser_ingress {
2602                let mut ingress = crate::browser::lock_ingress(ingress);
2603                match &error {
2604                    sipx_rtp::srtp::SrtpError::TooShort(_) => {
2605                        ingress.note_malformed(crate::browser::IngressClass::Srtp);
2606                    }
2607                    sipx_rtp::srtp::SrtpError::Replayed(_) => {
2608                        ingress.note_replay(crate::browser::IngressClass::Srtp);
2609                    }
2610                    _ => ingress.note_authentication_failure(crate::browser::IngressClass::Srtp),
2611                }
2612            }
2613            // discard: `srtp_unprotect_failures` was incremented above; browser sessions also
2614            // classified the same refusal into malformed, replay or authentication exactly once.
2615            tracing::debug!(%error, %source, "dropping a packet that failed SRTP");
2616            None
2617        }
2618    }
2619}
2620
2621/// An SRTP context from a master key and salt, or `None` when the media is not encrypted.
2622fn srtp_context(keys: Option<&(Vec<u8>, Vec<u8>)>) -> Option<sipx_rtp::SrtpContext> {
2623    let (key, salt) = keys?;
2624    match sipx_rtp::SrtpContext::new(key, salt) {
2625        Ok(context) => Some(context),
2626        Err(error) => {
2627            // discard: this refuses configuration before any packet exists; runtime discard
2628            // counters count media the path was actually handed, not setup values.
2629            // Carrying on unencrypted would be the worst of the three options: the far end
2630            // expects SRTP, so the media is useless to it *and* readable to everyone else.
2631            tracing::error!(%error, "SRTP keys were refused; this session will carry nothing");
2632            None
2633        }
2634    }
2635}
2636
2637/// Bind the control port for a media port, if it is free.
2638async fn bind_control_port(media: SocketAddr) -> Option<Arc<UdpSocket>> {
2639    let port = media.port().checked_add(1)?;
2640    UdpSocket::bind(SocketAddr::new(media.ip(), port))
2641        .await
2642        .ok()
2643        .map(Arc::new)
2644}
2645
2646#[allow(clippy::too_many_arguments)]
2647/// The RTP clock for one outgoing stream.
2648///
2649/// One type rather than a pile of locals, because these six values are one thing: a tone and
2650/// the audio around it share a timeline, and the bookkeeping that keeps them from overlapping
2651/// is the fiddliest part of sending. Keeping it here leaves the send loop about pacing, which
2652/// is what the send loop is about.
2653struct SendClock {
2654    sequence: u16,
2655    timestamp: u32,
2656    /// The tone in progress, if any, and the timestamp it started at.
2657    current_tone: Option<u64>,
2658    tone_started_at: Option<u32>,
2659    /// What the clock owes the tone in progress, charged when it is over.
2660    tone_duration: u32,
2661    /// Whether the next audio packet begins a new talkspurt — RFC 3550's marker bit says so,
2662    /// and a tone interrupts the audio.
2663    ending_a_tone: bool,
2664}
2665
2666impl SendClock {
2667    /// A clock starting at a random point, as RFC 3550 §5.1 requires of both counters.
2668    fn new() -> Self {
2669        Self {
2670            sequence: rand::random(),
2671            timestamp: rand::random(),
2672            current_tone: None,
2673            tone_started_at: None,
2674            tone_duration: 0,
2675            ending_a_tone: false,
2676        }
2677    }
2678
2679    /// Move past a packet that has been sent. Both counters wrap, and both are supposed to.
2680    fn advance(&mut self, samples: u32) {
2681        self.sequence = self.sequence.wrapping_add(1);
2682        self.timestamp = self.timestamp.wrapping_add(samples);
2683    }
2684
2685    /// Build one packet of audio.
2686    ///
2687    /// `None` when the codec refused the frame, which is not the same as an empty packet: an
2688    /// empty packet would have the far end decode nothing as audio.
2689    fn audio(
2690        &mut self,
2691        encoding: &mut Encoding,
2692        payload_type: u8,
2693        ssrc: u32,
2694        samples: &[i16],
2695    ) -> Option<(Packet, u32)> {
2696        // A tone that has just finished owes the clock its duration; pay it before stamping the
2697        // audio that follows, or the audio overlaps the keypress.
2698        self.timestamp = self
2699            .timestamp
2700            .wrapping_add(std::mem::take(&mut self.tone_duration));
2701        self.current_tone = None;
2702        self.tone_started_at = None;
2703
2704        let encoded = encoding.encode(samples)?;
2705        let mut packet = Packet::new(
2706            payload_type,
2707            self.sequence,
2708            self.timestamp,
2709            ssrc,
2710            Bytes::from(encoded),
2711        );
2712        packet.marker = std::mem::take(&mut self.ending_a_tone);
2713
2714        // The timestamp advances by the samples this packet actually carried, not by the
2715        // configured packet size. They are usually the same, and when they are not — a caller
2716        // sending 10 ms frames on a 20 ms config — advancing by the configured size builds a
2717        // timeline at the wrong rate, and the far end plays the call with a gap between every
2718        // packet.
2719        Some((packet, u32::try_from(samples.len()).unwrap_or(0)))
2720    }
2721
2722    /// Build one packet of an RFC 4733 tone.
2723    fn tone(
2724        &mut self,
2725        payload_type: u8,
2726        ssrc: u32,
2727        event: DtmfEvent,
2728        offset: u32,
2729        tone: u64,
2730    ) -> (Packet, u32) {
2731        // A new keypress starts here; anything with the same tag continues the one in progress
2732        // and reuses its timestamp. That shared timestamp is what marks the packets as one
2733        // press — including the end retransmissions, which is the case that gets this wrong.
2734        let starting = self.current_tone != Some(tone);
2735        if starting {
2736            // The previous tone's duration is charged to the clock now, so audio resumes past
2737            // it rather than on top of it.
2738            self.timestamp = self
2739                .timestamp
2740                .wrapping_add(std::mem::take(&mut self.tone_duration));
2741            self.current_tone = Some(tone);
2742            self.tone_started_at = Some(self.timestamp);
2743        }
2744
2745        // Within a keypress, the segment offset moves the stamp: a segment past the duration
2746        // field's range starts at its own timestamp (RFC 4733 §2.5.1.3). The marker stays on
2747        // the event's first packet only — a segment continues a keypress, it does not start one.
2748        let mut packet = Packet::new(
2749            payload_type,
2750            self.sequence,
2751            self.tone_started_at
2752                .unwrap_or(self.timestamp)
2753                .wrapping_add(offset),
2754            ssrc,
2755            event.encode(),
2756        );
2757        packet.marker = starting;
2758        if event.end {
2759            // The whole event's length: the final segment's start plus what it carried.
2760            self.tone_duration = offset.wrapping_add(u32::from(event.duration));
2761            self.ending_a_tone = true;
2762        }
2763        (packet, 0)
2764    }
2765}
2766
2767/// Everything the send loop needs beyond its socket and its channel.
2768struct Sending {
2769    remote: Arc<Mutex<SocketAddr>>,
2770    config: Config,
2771    ssrc: u32,
2772    sent: Arc<AtomicU64>,
2773    outbound: Arc<Outbound>,
2774    muted: Arc<AtomicBool>,
2775    /// Where the fact that a packet went out is reported, so §11's keepalive is only sent on a
2776    /// pair that has actually been quiet for Tr.
2777    ice: Option<crate::ice::driver::Handle>,
2778    stop: Arc<Stop>,
2779    /// Constructed before this worker is spawned, so startup cannot fail inside the task.
2780    encoding: Encoding,
2781    discards: Arc<DiscardMeters>,
2782}
2783
2784// This is the single owner of the RTP send sequence, codec, SRTP context, pacing and their discard
2785// meters. Splitting those state transitions across helpers would make their ordering harder to audit.
2786#[allow(clippy::too_many_lines)]
2787async fn send_loop(socket: Arc<UdpSocket>, mut outgoing: mpsc::Receiver<Frame>, sending: Sending) {
2788    let Sending {
2789        remote,
2790        config,
2791        ssrc,
2792        sent,
2793        outbound,
2794        muted,
2795        ice,
2796        stop,
2797        mut encoding,
2798        discards,
2799    } = sending;
2800    let mut clock = SendClock::new();
2801    // One context, owned by this loop. SRTP keeps a rollover counter and a replay window per
2802    // stream, and a context behind a lock would put a mutex in the packet path for state exactly
2803    // one task ever touches.
2804    let mut protect = srtp_context(config.srtp.as_ref().map(|keys| &keys.local));
2805
2806    // One clock for the whole stream. Sending on channel readiness instead makes the packet
2807    // rate depend on how fast the application produces samples.
2808    let mut tick = tokio::time::interval(config.packet_duration);
2809    tick.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Delay);
2810
2811    loop {
2812        if stop.is_stopped() {
2813            return;
2814        }
2815        tokio::select! {
2816            () = stop.wait() => return,
2817            _ = tick.tick() => {}
2818        }
2819
2820        let Some(frame) = next_frame(&mut outgoing, &stop).await else {
2821            return;
2822        };
2823
2824        // The mute gate goes here — before the packet is built, and therefore before the
2825        // sequence number, the send counters and the sender report's octet count have been moved
2826        // by it. See [`gated`].
2827        let frame = gated(frame, &muted, config.samples_per_packet());
2828
2829        let (packet, advance) = match &frame {
2830            Frame::Audio { samples, .. } => {
2831                let Some(built) =
2832                    clock.audio(&mut encoding, config.wire_payload_type(), ssrc, samples)
2833                else {
2834                    discards
2835                        .opus_encode_failures
2836                        .fetch_add(1, Ordering::Relaxed);
2837                    continue;
2838                };
2839                built
2840            }
2841            Frame::Encoded {
2842                payload_type,
2843                payload,
2844            } => {
2845                // Verbatim, on this leg's own sequence and timestamp. The advance is the
2846                // configured packet size: the bytes came from a stream with the same
2847                // packetisation, and nothing here can look inside them to check.
2848                let packet = Packet::new(
2849                    *payload_type,
2850                    clock.sequence,
2851                    clock.timestamp,
2852                    ssrc,
2853                    payload.clone(),
2854                );
2855                (
2856                    packet,
2857                    u32::try_from(config.samples_per_packet()).unwrap_or(0),
2858                )
2859            }
2860            Frame::Dtmf {
2861                event,
2862                offset,
2863                tone,
2864            } => {
2865                let Some(payload_type) = config.dtmf_payload_type else {
2866                    // Nothing negotiated `telephone-event`, so there is no payload type to send
2867                    // it on. Dropping is right: guessing one means sending keypresses on
2868                    // whatever the far end uses that number for.
2869                    continue;
2870                };
2871                clock.tone(payload_type, ssrc, *event, *offset, *tone)
2872            }
2873        };
2874
2875        let destination = *remote.lock().await;
2876        let payload_len = packet.payload.len();
2877        let encoded = packet.encode();
2878        let datagram = match protect.as_mut() {
2879            Some(context) => match context.protect(&encoded) {
2880                Ok(protected) => Bytes::from(protected),
2881                Err(error) => {
2882                    // Sending it in the clear instead is not an option: the far end negotiated
2883                    // encryption and a cleartext packet is both unreadable to it and readable to
2884                    // everyone else.
2885                    // discard: `Packet::encode` always makes the complete header `protect`
2886                    // requires, so this error branch is structurally unreachable here.
2887                    tracing::warn!(%error, "dropping a packet SRTP could not protect");
2888                    continue;
2889                }
2890            },
2891            None => encoded,
2892        };
2893        if socket.send_to(&datagram, destination).await.is_err() {
2894            return;
2895        }
2896        // §11: a keepalive goes out only when nothing has been sent on the selected pair for Tr,
2897        // so the agent has to be told that something was. Reported after the send and not before,
2898        // because a packet that did not leave has not held the binding open.
2899        if let Some(handle) = &ice {
2900            handle.data_sent(ComponentId::RTP);
2901        }
2902        sent.fetch_add(1, Ordering::Relaxed);
2903        // What a sender report describes. The octet count is payload only, headers excluded
2904        // (RFC 3550 §6.4.1) — counting headers would overstate the bandwidth by a fifth on
2905        // 20 ms G.711 and by far more on anything smaller.
2906        outbound.packets.fetch_add(1, Ordering::Relaxed);
2907        outbound
2908            .octets
2909            .fetch_add(payload_len as u64, Ordering::Relaxed);
2910        outbound
2911            .timestamp
2912            .store(packet.timestamp, Ordering::Relaxed);
2913
2914        // Both counters wrap, and both are supposed to.
2915        //
2916        // The timestamp advances by the samples this packet actually carried, not by the
2917        // configured packet size. They are usually the same, and when they are not — a caller
2918        // sending 10 ms frames on a 20 ms config — advancing by the configured size builds a
2919        // timeline at the wrong rate, and the far end plays the call with a gap between every
2920        // packet.
2921        clock.advance(advance);
2922    }
2923}
2924
2925/// The next frame the send loop should put on the wire, or `None` when there will not be another.
2926///
2927/// Both awaits check the stop signal. A loop parked on its channel when the call is hung up would
2928/// otherwise go on sending audio into a torn-down call.
2929///
2930/// Frames belonging to a stopped playback are skipped here rather than sent, and skipping one
2931/// costs nothing — this takes the next frame straight away rather than returning to the caller's
2932/// pacing tick. That is what bounds a stop at [`Playback::STOP_BOUND_PACKETS`]: whatever backlog
2933/// the queue was holding for the stopped clip drains inside one packet interval, and none of it
2934/// reaches the wire.
2935async fn next_frame(outgoing: &mut mpsc::Receiver<Frame>, stop: &Stop) -> Option<Frame> {
2936    loop {
2937        let frame = tokio::select! {
2938            () = stop.wait() => return None,
2939            received = outgoing.recv() => received?,
2940        };
2941        if stop.is_stopped() {
2942            return None;
2943        }
2944        if !discarded(&frame) {
2945            return Some(frame);
2946        }
2947    }
2948}
2949
2950/// One frame as the session actually sends it: itself, or what a muted session puts in its place
2951/// (`M-18`).
2952///
2953/// Applied *before* the packet is built, which is the part RFC 3550 §6 constrains: a mute that
2954/// dropped the finished datagram at the socket instead would leave the sequence number, the send
2955/// counters and the sender report's octet count (§6.4.1) all describing packets that never went
2956/// out, and would open a sequence gap the far end scores as loss.
2957///
2958/// Audio becomes the same number of samples of silence, so the timestamp this frame advances the
2959/// clock by is the one it would have advanced it by unmuted — the far end's timeline does not
2960/// move under it.
2961///
2962/// A relayed payload ([`Frame::Encoded`], a bridge passing bytes across) becomes one packet's
2963/// worth of silence in *this* session's codec. Its bytes cannot be silenced in place: they are an
2964/// opaque payload in whatever the other leg negotiated, and there is nothing here that can look
2965/// inside them. Substituting this session's own silence keeps the leg saying nothing rather than
2966/// saying what the muted party said.
2967///
2968/// A telephone event passes through. It is not audio: it is generated by this endpoint on
2969/// purpose, the way a keypad tone is on a handset, and a mute that swallowed keypresses would
2970/// make a muted caller unable to answer an IVR.
2971fn gated(frame: Frame, muted: &AtomicBool, samples_per_packet: usize) -> Frame {
2972    if !muted.load(Ordering::SeqCst) {
2973        return frame;
2974    }
2975    match frame {
2976        Frame::Audio { samples, playback } => Frame::Audio {
2977            samples: vec![0; samples.len()],
2978            playback,
2979        },
2980        Frame::Encoded { .. } => Frame::Audio {
2981            samples: vec![0; samples_per_packet],
2982            playback: None,
2983        },
2984        event @ Frame::Dtmf { .. } => event,
2985    }
2986}
2987
2988/// Whether this frame belongs to a playback that has been stopped, and so must not be sent
2989/// (`M-17`).
2990///
2991/// Read *before* the packet is built, for the same RFC 3550 §6 reason the mute gate is
2992/// ([`gated`]): the sequence number, the send counters and the sender report's octet count must
2993/// describe packets that actually went out. A frame discarded here never touches any of them,
2994/// which leaves the stream in exactly the state it is in whenever the application has nothing to
2995/// say — no gap for the far end to score as loss, because a gap needs a sequence number that was
2996/// allocated and never sent.
2997fn discarded(frame: &Frame) -> bool {
2998    matches!(frame, Frame::Audio { playback: Some(playback), .. } if playback.is_stopped())
2999}
3000
3001/// Start the playback queue and hand back the end a session keeps (`M-17`).
3002fn spawn_playback_queue(
3003    outgoing: &mpsc::Sender<Frame>,
3004    stop: &Arc<Stop>,
3005) -> (mpsc::Sender<Clip>, tokio::task::JoinHandle<()>) {
3006    let (clips_tx, clips_rx) = mpsc::channel::<Clip>(Playback::QUEUE_DEPTH);
3007    let owner = tokio::spawn(playback_loop(clips_rx, outgoing.clone(), Arc::clone(stop)));
3008    (clips_tx, owner)
3009}
3010
3011#[cfg(feature = "dtls")]
3012fn spawn_browser_playback_queue(
3013    outgoing: &mpsc::Sender<Frame>,
3014    stop: &Arc<Stop>,
3015    profile_tasks: Arc<crate::browser::ProfileTasks>,
3016) -> (mpsc::Sender<Clip>, tokio::task::JoinHandle<()>) {
3017    let (clips_tx, clips_rx) = mpsc::channel::<Clip>(Playback::QUEUE_DEPTH);
3018    let owner = tokio::spawn(crate::browser::profile_task(
3019        profile_tasks,
3020        playback_loop(clips_rx, outgoing.clone(), Arc::clone(stop)),
3021    ));
3022    (clips_tx, owner)
3023}
3024
3025/// The playback queue: one clip at a time, in the order they were started (`M-17`).
3026///
3027/// One task owns it, which is what makes "clips queue" true by construction: the order clips are
3028/// handed to the channel is the order they reach the send queue, and no two of them can ever
3029/// interleave their packets.
3030///
3031/// A task rather than a lock around the send queue. One owner is what makes the ordering a
3032/// property of the type instead of something every caller has to be careful about: two clips
3033/// started at once cannot interleave their packets, and the order they were started in is the
3034/// order the far end hears them, whatever order the callers' tasks happened to be scheduled in.
3035async fn playback_loop(
3036    mut clips: mpsc::Receiver<Clip>,
3037    outgoing: mpsc::Sender<Frame>,
3038    stop: Arc<Stop>,
3039) {
3040    loop {
3041        let clip = tokio::select! {
3042            () = stop.wait() => return,
3043            next = clips.recv() => match next {
3044                Some(clip) => clip,
3045                None => return,
3046            },
3047        };
3048
3049        let end = feed(&clip, &outgoing, &stop).await;
3050        clip.finish(end);
3051        if end == PlaybackEnd::SessionEnded {
3052            // Whatever is still queued goes down with the receiver, and every handle waiting on
3053            // one of those clips learns the same thing when its sender drops.
3054            return;
3055        }
3056    }
3057}
3058
3059/// Hand one clip to the send queue, packet by packet, until it runs out or something cuts it
3060/// short.
3061async fn feed(clip: &Clip, outgoing: &mpsc::Sender<Frame>, stop: &Stop) -> PlaybackEnd {
3062    // Armed at the head of the queue, not when the clip was started: a key pressed while an
3063    // earlier clip was still playing belongs to that clip. Reading the counter here is what marks
3064    // everything before this moment as already seen.
3065    let mut keypresses = match clip.interrupt {
3066        Interrupt::OnDigit => {
3067            let mut keypresses = clip.keypresses.clone();
3068            let _seen = *keypresses.borrow_and_update();
3069            Some(keypresses)
3070        }
3071        Interrupt::Never => None,
3072    };
3073
3074    for chunk in clip.samples.chunks(clip.samples_per_packet) {
3075        if clip.stop.is_stopped() {
3076            return PlaybackEnd::Stopped;
3077        }
3078        let mut samples = chunk.to_vec();
3079        // The last chunk may be short. Padding with silence keeps every packet the same size,
3080        // which is what a far-end jitter buffer expects.
3081        samples.resize(clip.samples_per_packet, 0);
3082        let frame = Frame::Audio {
3083            samples,
3084            playback: Some(Arc::clone(&clip.stop)),
3085        };
3086
3087        // Biased so that a stop or a keypress wins over queueing one more packet. Without it a
3088        // clip whose send queue happens to have room would go on feeding it for as long as the
3089        // scheduler kept picking that branch.
3090        tokio::select! {
3091            biased;
3092            () = stop.wait() => return PlaybackEnd::SessionEnded,
3093            () = clip.stop.wait() => return PlaybackEnd::Stopped,
3094            () = keypress(keypresses.as_mut()) => {
3095                // Set here rather than left to the caller: it is what tells the send loop to
3096                // discard the packets of this clip it is already holding, and until it is set
3097                // they would go out.
3098                clip.stop.stop();
3099                return PlaybackEnd::Interrupted;
3100            }
3101            queued = outgoing.send(frame) => {
3102                if queued.is_err() {
3103                    return PlaybackEnd::SessionEnded;
3104                }
3105            }
3106        }
3107    }
3108    PlaybackEnd::Completed
3109}
3110
3111/// Resolve when the far end presses a key, or never.
3112///
3113/// Never in two cases, and they are the same case to a caller: the clip was not started
3114/// interruptible, or the receive loop is gone — in which case no keypress is coming and treating
3115/// the channel's closure as one would cut every remaining clip short at the end of a call.
3116async fn keypress(keypresses: Option<&mut watch::Receiver<u64>>) {
3117    if let Some(keypresses) = keypresses
3118        && keypresses.changed().await.is_ok()
3119    {
3120        return;
3121    }
3122    std::future::pending::<()>().await;
3123}
3124
3125/// Where a keypress goes: the application's channel, and the tick an interruptible playback
3126/// watches.
3127///
3128/// One type rather than two parameters, because the ordering between them is load-bearing — see
3129/// [`deliver`].
3130struct Keypresses {
3131    to: mpsc::Sender<(Digit, Duration)>,
3132    arrivals: Arc<watch::Sender<u64>>,
3133}
3134
3135/// Everything the receive loop needs, grouped because eight positional arguments is a
3136/// mis-ordering waiting to happen — two of them are `Arc<AtomicU64>`-shaped and swapping them
3137/// would compile.
3138struct Inbound {
3139    audio: mpsc::Sender<Vec<i16>>,
3140    encoded: mpsc::Sender<Encoded>,
3141    relay: Arc<AtomicBool>,
3142    digits: Keypresses,
3143    remote: Arc<Mutex<SocketAddr>>,
3144    config: Config,
3145    received: Arc<AtomicU64>,
3146    stats: Arc<Mutex<StreamStats>>,
3147    rtcp_observation: RtcpObservation,
3148    ssrc: u32,
3149    /// Whether the first packet's source replaces the advertised address (symmetric RTP).
3150    ///
3151    /// False for a stream ICE is driving: RFC 8445 §8.1.1's selected pair replaces this, and it
3152    /// has to *replace* it rather than race it — a stream that also learned from the first RTP
3153    /// packet to arrive would let an off-path sender who guessed the port undo the one thing a
3154    /// checked path bought.
3155    symmetric: bool,
3156    /// Where a STUN datagram goes (RFC 5764 §5.1.2), when ICE is running.
3157    ice: Option<crate::ice::driver::Handle>,
3158    browser_ingress: Option<Arc<std::sync::Mutex<crate::browser::ComponentIngress>>>,
3159    stop: Arc<Stop>,
3160    /// Constructed before this worker is spawned, so startup cannot fail inside the task.
3161    decoding: Decoding,
3162    discards: Arc<DiscardMeters>,
3163}
3164
3165/// Split a datagram arriving on a port that carries media three ways (RFC 5764 §5.1.2).
3166///
3167/// The first byte decides, and it decides **before anything else looks at the datagram**: a
3168/// connectivity check must never reach the jitter buffer and an RTP packet must never reach the
3169/// ICE agent. Returns the bytes only for the RTP path; a check goes to the agent and anything
3170/// else is dropped by name rather than handed to whichever parser happens to be first.
3171///
3172/// A check is dropped rather than kept when no agent is running, which is what the media loops
3173/// did with one before ICE existed — it would fail to parse as RTP one line later.
3174fn demultiplex<'a>(
3175    datagram: &'a [u8],
3176    from: SocketAddr,
3177    on: ice::LocalBase,
3178    ice: Option<&ice::driver::Handle>,
3179) -> Option<&'a [u8]> {
3180    match crate::dtls::classify(datagram) {
3181        crate::dtls::Arriving::Rtp => Some(datagram),
3182        crate::dtls::Arriving::Stun => {
3183            if let Some(handle) = ice {
3184                handle.datagram(from, on, datagram.to_vec());
3185            }
3186            None
3187        }
3188        crate::dtls::Arriving::Dtls | crate::dtls::Arriving::Unknown => None,
3189    }
3190}
3191
3192/// The RFC 5761 second-stage class inside RFC 5764's RTP-or-RTCP first-byte range.
3193#[derive(Debug, Clone, Copy, PartialEq, Eq)]
3194enum MuxedPacket<'a> {
3195    Rtp(&'a [u8]),
3196    Rtcp(&'a [u8]),
3197}
3198
3199fn classify_muxed(datagram: &[u8]) -> Option<MuxedPacket<'_>> {
3200    let packet_type = *datagram.get(1)?;
3201    if (192..=223).contains(&packet_type) {
3202        Some(MuxedPacket::Rtcp(datagram))
3203    } else {
3204        Some(MuxedPacket::Rtp(datagram))
3205    }
3206}
3207
3208/// Everything the two RTCP loops need, grouped because they share most of it.
3209struct Control {
3210    media: Arc<UdpSocket>,
3211    rtcp: Option<Arc<UdpSocket>>,
3212    remote: Arc<Mutex<SocketAddr>>,
3213    rtcp_remote: Arc<Mutex<Option<SocketAddr>>>,
3214    interval: Option<Duration>,
3215    mode: sipx_sdp::RtcpMode,
3216    ssrc: u32,
3217    cname: String,
3218    stats: Arc<Mutex<StreamStats>>,
3219    outbound: Arc<Outbound>,
3220    rtcp_observation: RtcpObservation,
3221    srtp: Option<SrtpKeys>,
3222    ice: Option<ice::driver::Handle>,
3223    stop: Arc<Stop>,
3224    discards: Arc<DiscardMeters>,
3225    #[cfg(feature = "dtls")]
3226    profile_tasks: Option<Arc<crate::browser::ProfileTasks>>,
3227}
3228
3229/// Start the report loops, as far as this session's configuration and sockets allow.
3230fn spawn_control(control: Control) -> Vec<tokio::task::JoinHandle<()>> {
3231    let mut owners = Vec::with_capacity(2);
3232    #[cfg(feature = "dtls")]
3233    let profile_tasks = control.profile_tasks.clone();
3234    if let Some(interval) = control.interval {
3235        let reporter = rtcp_loop(
3236            // Reports go out from the control port when there is one, which is what a peer
3237            // expects to see them come from; from the media port otherwise, which some peers
3238            // will refuse but is better than not reporting at all.
3239            control.rtcp.clone().unwrap_or(control.media),
3240            control.remote,
3241            control.rtcp_remote,
3242            interval,
3243            control.mode,
3244            control.ssrc,
3245            control.cname,
3246            control.stats,
3247            control.outbound,
3248            Arc::clone(&control.rtcp_observation.feedback),
3249            control.srtp.clone(),
3250            Arc::clone(&control.stop),
3251        );
3252        #[cfg(feature = "dtls")]
3253        let owner = if let Some(tasks) = profile_tasks.clone() {
3254            tokio::spawn(crate::browser::profile_task(tasks, reporter))
3255        } else {
3256            tokio::spawn(reporter)
3257        };
3258        #[cfg(not(feature = "dtls"))]
3259        let owner = tokio::spawn(reporter);
3260        owners.push(owner);
3261    }
3262    if control.mode == sipx_sdp::RtcpMode::Separate
3263        && let Some(port) = control.rtcp
3264    {
3265        let receiver = rtcp_receive_loop(
3266            port,
3267            control.ssrc,
3268            control.rtcp_observation,
3269            control.srtp,
3270            control.ice,
3271            control.stop,
3272            control.discards,
3273        );
3274        #[cfg(feature = "dtls")]
3275        let owner = if let Some(tasks) = profile_tasks {
3276            tokio::spawn(crate::browser::profile_task(tasks, receiver))
3277        } else {
3278            tokio::spawn(receiver)
3279        };
3280        #[cfg(not(feature = "dtls"))]
3281        let owner = tokio::spawn(receiver);
3282        owners.push(owner);
3283    }
3284    owners
3285}
3286
3287/// Start the ICE driver for this session, over the sockets the session is running on.
3288///
3289/// The base numbering is the one gathering used and the only one there is: base 0 is the media
3290/// socket, base 1 the control port when there is one. The agent names a socket by that index
3291/// alone (`docs/specs/ice.md` §2), so the two lists have to be built the same way in both places
3292/// or a check leaves the wrong port.
3293fn spawn_ice(
3294    local: ice::LocalDescription,
3295    socket: &Arc<UdpSocket>,
3296    rtcp: Option<&Arc<UdpSocket>>,
3297    destinations: &ice::driver::Destinations,
3298    stop: &Arc<Stop>,
3299    discards: &Arc<DiscardMeters>,
3300) -> (ice::driver::Handle, tokio::task::JoinHandle<()>) {
3301    let (agent, pending) = local.into_driver_parts();
3302    let mut sockets = vec![Arc::clone(socket)];
3303    if let Some(control) = rtcp {
3304        sockets.push(Arc::clone(control));
3305    }
3306    ice::driver::spawn(
3307        agent,
3308        pending,
3309        sockets,
3310        destinations.clone(),
3311        Arc::clone(stop),
3312        Arc::clone(discards),
3313    )
3314}
3315
3316/// Decide whether a packet belongs to the stream this session is carrying.
3317///
3318/// RTP has no authentication, so this is not a security control — anyone who can guess the port
3319/// can still forge a first packet. What it buys is that once a stream is established, a *later*
3320/// forged packet with a different SSRC cannot redirect our media or poison the jitter buffer,
3321/// which is the difference between a race an attacker has to win and one they can win at
3322/// leisure.
3323async fn accept_source(
3324    stream: &mut Option<u32>,
3325    packet: &Packet,
3326    source: SocketAddr,
3327    remote: &Arc<Mutex<SocketAddr>>,
3328    stats: &Arc<Mutex<StreamStats>>,
3329    symmetric: bool,
3330    discards: &DiscardMeters,
3331) -> bool {
3332    match *stream {
3333        None => {
3334            // Symmetric RTP: the observed source replaces the advertised address, because
3335            // behind a NAT the advertised one is private and this is the only path back.
3336            // Deliberately after the packet parses, so a stray STUN probe cannot move it.
3337            //
3338            // Not for a stream ICE is driving. There the address is the selected pair's
3339            // (RFC 8445 §8.1.1) and an unauthenticated packet must not be able to move it —
3340            // `docs/specs/ice.md` §11.3. The SSRC is still learned either way, because that is
3341            // what keeps a second source out of the jitter buffer.
3342            if symmetric {
3343                *remote.lock().await = source;
3344            }
3345            *stream = Some(packet.ssrc);
3346            // The far end names itself in its first packet, and the statistics carry that name
3347            // into every report block (RFC 3550 §6.4.1: a block's SSRC is the source it
3348            // describes).
3349            stats.lock().await.set_ssrc(packet.ssrc);
3350            true
3351        }
3352        Some(established) if established != packet.ssrc => {
3353            // Another source on our port. Dropped rather than mixed in: one packet with a high
3354            // sequence number would otherwise advance the jitter buffer past every genuine
3355            // packet still to come, and the call goes silent.
3356            discards.foreign_ssrc.fetch_add(1, Ordering::Relaxed);
3357            tracing::debug!(
3358                %source,
3359                ssrc = packet.ssrc,
3360                "ignoring a packet from a different synchronisation source"
3361            );
3362            false
3363        }
3364        Some(_) => true,
3365    }
3366}
3367
3368enum ReceiveInput {
3369    Socket {
3370        socket: Arc<UdpSocket>,
3371        datagram: Vec<u8>,
3372    },
3373    #[cfg(feature = "dtls")]
3374    Browser(crate::browser::MediaIngress),
3375}
3376
3377enum ReceivedDatagram {
3378    Rtp { source: SocketAddr, bytes: Vec<u8> },
3379    Rtcp { bytes: Vec<u8> },
3380    Silence,
3381    Closed,
3382}
3383
3384impl ReceiveInput {
3385    fn socket(socket: Arc<UdpSocket>) -> Self {
3386        Self::Socket {
3387            socket,
3388            datagram: vec![0u8; 2048],
3389        }
3390    }
3391
3392    async fn next(
3393        &mut self,
3394        flush_after: Duration,
3395        config: &Config,
3396        ice: Option<&crate::ice::driver::Handle>,
3397        stop: &Stop,
3398    ) -> ReceivedDatagram {
3399        loop {
3400            match self {
3401                Self::Socket { socket, datagram } => {
3402                    let read = tokio::select! {
3403                        () = stop.wait() => return ReceivedDatagram::Closed,
3404                        read = tokio::time::timeout(flush_after, socket.recv_from(datagram)) => read,
3405                    };
3406                    let (length, source) = match read {
3407                        Ok(Ok(received)) => received,
3408                        Ok(Err(_)) => return ReceivedDatagram::Closed,
3409                        Err(_elapsed) => return ReceivedDatagram::Silence,
3410                    };
3411                    let arrived = datagram.get(..length).unwrap_or_default();
3412                    let Some(media) = demultiplex(arrived, source, crate::ice::LocalBase(0), ice)
3413                    else {
3414                        continue;
3415                    };
3416                    if config.rtcp_mode == sipx_sdp::RtcpMode::Mux {
3417                        match classify_muxed(media) {
3418                            Some(MuxedPacket::Rtp(media)) => {
3419                                return ReceivedDatagram::Rtp {
3420                                    source,
3421                                    bytes: media.to_vec(),
3422                                };
3423                            }
3424                            Some(MuxedPacket::Rtcp(control)) => {
3425                                return ReceivedDatagram::Rtcp {
3426                                    bytes: control.to_vec(),
3427                                };
3428                            }
3429                            None => continue,
3430                        }
3431                    }
3432                    return ReceivedDatagram::Rtp {
3433                        source,
3434                        bytes: media.to_vec(),
3435                    };
3436                }
3437                #[cfg(feature = "dtls")]
3438                Self::Browser(media) => {
3439                    let next = tokio::select! {
3440                        () = stop.wait() => return ReceivedDatagram::Closed,
3441                        next = tokio::time::timeout(flush_after, async {
3442                            tokio::select! {
3443                                packet = media.srtp.recv() => packet.map(|packet| (false, packet)),
3444                                packet = media.srtcp.recv() => packet.map(|packet| (true, packet)),
3445                            }
3446                        }) => next,
3447                    };
3448                    return match next {
3449                        Err(_elapsed) => ReceivedDatagram::Silence,
3450                        Ok(Some((true, packet))) => ReceivedDatagram::Rtcp {
3451                            bytes: packet.bytes,
3452                        },
3453                        Ok(Some((false, packet))) => ReceivedDatagram::Rtp {
3454                            source: packet.source,
3455                            bytes: packet.bytes,
3456                        },
3457                        Ok(None) => ReceivedDatagram::Closed,
3458                    };
3459                }
3460            }
3461        }
3462    }
3463}
3464
3465// This is the single ordered path from demultiplexing through authentication, source pinning,
3466// statistics and delivery. Its length keeps that security-sensitive order visible in one place.
3467#[allow(clippy::too_many_lines)]
3468async fn receive_loop(mut input: ReceiveInput, inbound: Inbound) {
3469    let Inbound {
3470        audio: incoming,
3471        encoded,
3472        relay,
3473        digits,
3474        remote,
3475        config,
3476        received,
3477        stats,
3478        rtcp_observation,
3479        ssrc,
3480        symmetric,
3481        ice,
3482        browser_ingress,
3483        stop,
3484        mut decoding,
3485        discards,
3486    } = inbound;
3487    let mut buffer = match config.jitter_max_depth {
3488        Some(max) => JitterBuffer::adaptive(config.jitter_depth, max),
3489        None => JitterBuffer::new(config.jitter_depth),
3490    };
3491    let mut unprotect = srtp_context(config.srtp.as_ref().map(|keys| &keys.remote));
3492    let mut unprotect_rtcp = srtp_context(config.srtp.as_ref().map(|keys| &keys.remote));
3493    let mut dtmf = sipx_rtp::dtmf::Receiver::new();
3494    let started = tokio::time::Instant::now();
3495    // The synchronisation source this session is carrying; see `accept_source` for why one is
3496    // pinned at all, and for what it does and does not buy.
3497    let mut stream: Option<u32> = None;
3498
3499    // When the far end stops, whatever the buffer is still holding has to come out. Without
3500    // this the last `depth - 1` packets are never played: in a continuous call that is
3501    // invisible, but at the end of every clip it clips the tail off.
3502    let flush_after = config
3503        .packet_duration
3504        .saturating_mul(4)
3505        .max(Duration::from_millis(60));
3506
3507    loop {
3508        if stop.is_stopped() {
3509            return;
3510        }
3511        let (media, source) = match input.next(flush_after, &config, ice.as_ref(), &stop).await {
3512            ReceivedDatagram::Rtp { source, bytes } => (bytes, source),
3513            ReceivedDatagram::Rtcp { bytes } => {
3514                process_rtcp(
3515                    &bytes,
3516                    ssrc,
3517                    &rtcp_observation,
3518                    &mut unprotect_rtcp,
3519                    &discards,
3520                    browser_ingress.as_ref(),
3521                )
3522                .await;
3523                continue;
3524            }
3525            ReceivedDatagram::Closed => return,
3526            ReceivedDatagram::Silence => {
3527                // Silence. Release what is held rather than holding it against a packet that is
3528                // not coming — otherwise the last `depth - 1` packets of every clip are lost.
3529                if !flush(
3530                    &mut buffer,
3531                    &delivery(&incoming, &encoded, &relay, &discards),
3532                    &mut decoding,
3533                    &digits,
3534                    &mut dtmf,
3535                    &config,
3536                    &stop,
3537                )
3538                .await
3539                {
3540                    return;
3541                }
3542                continue;
3543            }
3544        };
3545        let bytes = Bytes::from(media);
3546        // Authenticated before it is parsed. A packet that fails is dropped and nothing about it
3547        // reaches the parser, the jitter buffer or the statistics — which is the point of
3548        // authenticating at all: forged packets must not be able to move any state.
3549        let Some(bytes) = authenticated(
3550            unprotect.as_mut(),
3551            bytes,
3552            source,
3553            &discards,
3554            browser_ingress.as_ref(),
3555        ) else {
3556            continue;
3557        };
3558        let Ok(packet) = Packet::decode(&bytes) else {
3559            // A malformed packet is dropped, not fatal. Media ports attract stray traffic —
3560            // STUN probes, port scans, the occasional scanner — and none of it should end a
3561            // call.
3562            if let Some(ingress) = &browser_ingress {
3563                crate::browser::lock_ingress(ingress)
3564                    .note_malformed(crate::browser::IngressClass::Srtp);
3565            }
3566            continue;
3567        };
3568
3569        if !accept_source(
3570            &mut stream,
3571            &packet,
3572            source,
3573            &remote,
3574            &stats,
3575            symmetric,
3576            &discards,
3577        )
3578        .await
3579        {
3580            continue;
3581        }
3582
3583        received.fetch_add(1, Ordering::Relaxed);
3584
3585        // The arrival clock has to be in the same units as the RTP timestamp — 8000 per second
3586        // for G.711 — or the jitter estimate measures the difference between two unit systems
3587        // rather than between two packets.
3588        // One arrival instant, used by both. Reading the clock twice would have the jitter
3589        // buffer and the RTCP report disagree about when the same packet turned up, by however
3590        // long the lock took — and the whole point of both numbers is that they are comparable.
3591        let arrival = arrival_in_timestamp_units(started, config.clock_rate);
3592        note_arrival(&stats, &packet, &config, arrival).await;
3593
3594        buffer.push_at(packet, arrival);
3595
3596        while let Some(packet) = buffer.pop() {
3597            if !deliver(
3598                &Delivery {
3599                    audio: &incoming,
3600                    encoded: &encoded,
3601                    relay: &relay,
3602                    discards: &discards,
3603                },
3604                &mut decoding,
3605                &digits,
3606                &mut dtmf,
3607                &config,
3608                &stop,
3609                &packet,
3610            )
3611            .await
3612            {
3613                return;
3614            }
3615        }
3616    }
3617}
3618
3619/// Hand one packet's audio to the application.
3620///
3621/// Returns whether the loop should keep running.
3622/// Score one arrival against the stream's statistics (RFC 3550 §6.4.1).
3623///
3624/// A telephone event's timestamp is the event's *start* (RFC 4733 §2.5.1.2) and not this
3625/// packet's sampling instant, so its transit grows per packet by design and would fabricate
3626/// jitter out of a keypress. It still counts for loss and sequence continuity.
3627async fn note_arrival(
3628    stats: &Arc<Mutex<StreamStats>>,
3629    packet: &Packet,
3630    config: &Config,
3631    arrival: u32,
3632) {
3633    let mut stats = stats.lock().await;
3634    if config.dtmf_payload_type == Some(packet.payload_type) {
3635        stats.on_untimed_packet(packet.sequence);
3636    } else {
3637        stats.on_packet(packet.sequence, packet.timestamp, arrival);
3638    }
3639}
3640
3641/// The local clock in RTP timestamp units.
3642fn arrival_in_timestamp_units(started: tokio::time::Instant, clock_rate: u32) -> u32 {
3643    let elapsed = started.elapsed().as_secs_f64() * f64::from(clock_rate);
3644    #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
3645    let units = elapsed as u64;
3646    u32::try_from(units & u64::from(u32::MAX)).unwrap_or(0)
3647}
3648
3649/// One report interval, randomized as RFC 3550 §6.3.1 requires.
3650///
3651/// The computed interval is scaled by a factor drawn uniformly from [0.5, 1.5] and divided
3652/// by e − 3/2 ≈ 1.21828. The randomness is what keeps the participants' reports from
3653/// falling into lockstep — a fixed timer synchronises with any peer that computed the same
3654/// interval — and the division compensates so the mean stays at the configured value.
3655fn randomized_rtcp_interval(base: Duration, unit: f64) -> Duration {
3656    const COMPENSATION: f64 = std::f64::consts::E - 1.5;
3657    // A draw is a number in [0, 1); anything else — including NaN — collapses to the
3658    // middle of the range rather than panicking the report loop.
3659    let unit = if unit.is_finite() {
3660        unit.clamp(0.0, 1.0)
3661    } else {
3662        0.5
3663    };
3664    base.mul_f64((0.5 + unit) / COMPENSATION)
3665}
3666
3667/// Send a report every interval, describing what we have sent and what we have heard.
3668///
3669/// A **sender** report when we have sent anything, a receiver report otherwise — RFC 3550 §6.4
3670/// draws that line, and it is not cosmetic. Only a sender report carries the NTP timestamp the
3671/// far end echoes back, so a session that only ever sent receiver reports could never be told
3672/// its own round-trip time.
3673#[allow(clippy::too_many_arguments)]
3674async fn rtcp_loop(
3675    socket: Arc<UdpSocket>,
3676    remote: Arc<Mutex<SocketAddr>>,
3677    rtcp_remote: Arc<Mutex<Option<SocketAddr>>>,
3678    interval: Duration,
3679    mode: sipx_sdp::RtcpMode,
3680    ssrc: u32,
3681    cname: String,
3682    stats: Arc<Mutex<StreamStats>>,
3683    outbound: Arc<Outbound>,
3684    feedback: Arc<Mutex<Feedback>>,
3685    srtp: Option<SrtpKeys>,
3686    stop: Arc<Stop>,
3687) {
3688    // Owned by this loop, like the RTP contexts: SRTCP keeps its own index, and one task sends.
3689    let mut protect = srtp_context(srtp.as_ref().map(|keys| &keys.local));
3690
3691    loop {
3692        // Drawn afresh every cycle: reusing one draw for the whole session would leave the
3693        // reports evenly spaced again, just at a different spacing.
3694        let wait = randomized_rtcp_interval(interval, rand::random::<f64>());
3695        tokio::select! {
3696            () = stop.wait() => return,
3697            () = tokio::time::sleep(wait) => {}
3698        }
3699        if stop.is_stopped() {
3700            return;
3701        }
3702
3703        let sent_packets = outbound.packets.load(Ordering::Relaxed);
3704        let block = {
3705            let mut stats = stats.lock().await;
3706            // Asked of the counters rather than of a report, because `report_block()` closes the
3707            // reporting interval and RFC 3550 §6.4.1 bounds that interval by a report *packet*
3708            // going out. A tick that turns out to have nothing to say must leave the window for
3709            // the tick that does.
3710            if stats.extended_highest_sequence() == 0 && sent_packets == 0 {
3711                None
3712            } else {
3713                Some(stats.report_block())
3714            }
3715        };
3716        // Nothing has happened in either direction, so there is nothing to report on.
3717        let Some(block) = block else {
3718            continue;
3719        };
3720        let heard_anything = block.extended_highest_sequence != 0;
3721
3722        // Echo the far end's last sender report and how long we have sat on it, so *it* can
3723        // measure the round trip. Without these two fields the exchange is one-way: we could
3724        // learn our own round-trip time and the far end never could.
3725        let block = if heard_anything {
3726            let echo = *feedback.lock().await;
3727            let mut block = block;
3728            block.last_sender_report = echo.last_sender_report;
3729            block.delay_since_last_sender_report = echo.received_at.map_or(0, |at| {
3730                // In units of 1/65536 of a second.
3731                let held = tokio::time::Instant::now().saturating_duration_since(at);
3732                u32::try_from((held.as_nanos() * 65_536) / 1_000_000_000).unwrap_or(u32::MAX)
3733            });
3734            vec![block]
3735        } else {
3736            Vec::new()
3737        };
3738
3739        let report = if sent_packets > 0 {
3740            Rtcp::Sender(sipx_rtp::rtcp::SenderReport {
3741                ssrc,
3742                ntp_timestamp: sipx_rtp::quality::ntp_now(),
3743                rtp_timestamp: outbound.timestamp.load(Ordering::Relaxed),
3744                packet_count: u32::try_from(sent_packets).unwrap_or(u32::MAX),
3745                octet_count: u32::try_from(outbound.octets.load(Ordering::Relaxed))
3746                    .unwrap_or(u32::MAX),
3747                reports: block,
3748            })
3749        } else {
3750            // The first word after the header is the SSRC of the packet's *sender* — us — not
3751            // of the stream being described (RFC 3550 §6.4.2); the described stream is named
3752            // inside the block.
3753            Rtcp::Receiver(ReceiverReport {
3754                ssrc,
3755                reports: block,
3756            })
3757        };
3758
3759        // Never a bare report: RFC 3550 §6.1 requires a compound of at least two packets
3760        // with an SDES CNAME in each.
3761        let datagram = Rtcp::encode_compound(&[report, Rtcp::Sdes(Sdes::cname(ssrc, &cname))]);
3762        // RFC 3711 §3.4. A report says who is talking to whom and how well — exactly the
3763        // metadata that encrypting the media was meant to withhold.
3764        let datagram = match protect.as_mut() {
3765            Some(context) => match context.protect_rtcp(&datagram) {
3766                Ok(protected) => Bytes::from(protected),
3767                Err(error) => {
3768                    // discard: the compound encoder above always emits the eight-byte header
3769                    // `protect_rtcp` requires, so this error branch is structurally unreachable.
3770                    tracing::warn!(%error, "dropping a report SRTCP could not protect");
3771                    continue;
3772                }
3773            },
3774            None => datagram,
3775        };
3776
3777        // RTCP conventionally travels on the RTP port plus one (RFC 3550 §11) — unless ICE has
3778        // selected a pair for component 2, in which case the checked path is where the reports
3779        // go and the convention is only what got them there.
3780        let destination = *remote.lock().await;
3781        let rtcp_to = if mode == sipx_sdp::RtcpMode::Mux {
3782            destination
3783        } else {
3784            let selected = *rtcp_remote.lock().await;
3785            selected.unwrap_or_else(|| {
3786                SocketAddr::new(destination.ip(), destination.port().saturating_add(1))
3787            })
3788        };
3789        if socket.send_to(&datagram, rtcp_to).await.is_err() {
3790            return;
3791        }
3792    }
3793}
3794
3795/// Take in what the far end sends back.
3796///
3797/// Two things come out of this. A **sender report** from the far end has to be remembered so
3798/// our next report can echo it, which is how the far end measures its round trip. A **report
3799/// block describing us** carries the far end's echo of *our* report, which is how we measure
3800/// ours.
3801async fn rtcp_receive_loop(
3802    socket: Arc<UdpSocket>,
3803    ssrc: u32,
3804    rtcp_observation: RtcpObservation,
3805    srtp: Option<SrtpKeys>,
3806    ice: Option<crate::ice::driver::Handle>,
3807    stop: Arc<Stop>,
3808    discards: Arc<DiscardMeters>,
3809) {
3810    let mut unprotect = srtp_context(srtp.as_ref().map(|keys| &keys.remote));
3811    let mut datagram = vec![0u8; 2048];
3812    loop {
3813        let read = tokio::select! {
3814            () = stop.wait() => return,
3815            read = socket.recv_from(&mut datagram) => read,
3816        };
3817        if stop.is_stopped() {
3818            return;
3819        }
3820        let Ok((len, source)) = read else {
3821            return;
3822        };
3823
3824        // The control port carries the same three protocols the media port does, because ICE
3825        // checks component 2 over it.
3826        let arrived = datagram.get(..len).unwrap_or(&[]);
3827        let Some(control) = demultiplex(arrived, source, ice::LocalBase(1), ice.as_ref()) else {
3828            continue;
3829        };
3830
3831        process_rtcp(
3832            control,
3833            ssrc,
3834            &rtcp_observation,
3835            &mut unprotect,
3836            &discards,
3837            None,
3838        )
3839        .await;
3840    }
3841}
3842
3843/// Authenticate, decode and apply one RTCP compound packet.
3844///
3845/// Called by either the adjacent control-port owner or the muxed media-port owner; there is never
3846/// more than one caller for a running session.
3847async fn process_rtcp(
3848    control: &[u8],
3849    ssrc: u32,
3850    rtcp_observation: &RtcpObservation,
3851    unprotect: &mut Option<sipx_rtp::SrtpContext>,
3852    discards: &DiscardMeters,
3853    browser_ingress: Option<&Arc<std::sync::Mutex<crate::browser::ComponentIngress>>>,
3854) {
3855    let bytes = Bytes::copy_from_slice(control);
3856    let bytes = match unprotect.as_mut() {
3857        Some(context) => match context.unprotect_rtcp(&bytes) {
3858            Ok(plain) => Bytes::from(plain),
3859            Err(error) => {
3860                discards
3861                    .srtcp_unprotect_failures
3862                    .fetch_add(1, Ordering::Relaxed);
3863                if let Some(ingress) = browser_ingress {
3864                    let mut ingress = crate::browser::lock_ingress(ingress);
3865                    match &error {
3866                        sipx_rtp::srtp::SrtpError::TooShort(_) => {
3867                            ingress.note_malformed(crate::browser::IngressClass::Srtcp);
3868                        }
3869                        sipx_rtp::srtp::SrtpError::ReplayedRtcp(_) => {
3870                            ingress.note_replay(crate::browser::IngressClass::Srtcp);
3871                        }
3872                        _ => {
3873                            ingress
3874                                .note_authentication_failure(crate::browser::IngressClass::Srtcp);
3875                        }
3876                    }
3877                }
3878                // discard: `srtcp_unprotect_failures` was incremented above; browser sessions
3879                // also classified the refusal into malformed, replay or authentication once.
3880                tracing::debug!(%error, "dropping a report that failed SRTCP");
3881                return;
3882            }
3883        },
3884        None => bytes,
3885    };
3886    // Malformed control input is a drop, not a session failure, on either socket shape.
3887    let Ok(packets) = Rtcp::decode_compound(&bytes) else {
3888        if let Some(ingress) = browser_ingress {
3889            crate::browser::lock_ingress(ingress)
3890                .note_malformed(crate::browser::IngressClass::Srtcp);
3891        }
3892        return;
3893    };
3894    if let Some(ingress) = browser_ingress {
3895        crate::browser::lock_ingress(ingress).note_srtcp_processed();
3896    }
3897
3898    let arrival = tokio::time::Instant::now();
3899    for packet in packets {
3900        match packet {
3901            Rtcp::Sender(report) => {
3902                {
3903                    let mut held = rtcp_observation.feedback.lock().await;
3904                    held.last_sender_report = sipx_rtp::quality::middle_32(report.ntp_timestamp);
3905                    held.received_at = Some(arrival);
3906                }
3907                note_quality(
3908                    report.ssrc,
3909                    feedback_of(&report.reports, ssrc),
3910                    &rtcp_observation.feedback,
3911                    &rtcp_observation.quality_hook,
3912                    rtcp_observation.clock_rate,
3913                )
3914                .await;
3915            }
3916            Rtcp::Receiver(report) => {
3917                note_quality(
3918                    report.ssrc,
3919                    feedback_of(&report.reports, ssrc),
3920                    &rtcp_observation.feedback,
3921                    &rtcp_observation.quality_hook,
3922                    rtcp_observation.clock_rate,
3923                )
3924                .await;
3925            }
3926            Rtcp::Sdes(_) | Rtcp::Other { .. } => {}
3927        }
3928    }
3929}
3930
3931/// The block in this report that describes *our* stream, if there is one.
3932///
3933/// A report may carry blocks about several sources. Taking the first regardless would have a
3934/// three-party call measuring its round trip to whoever happened to be listed first.
3935fn feedback_of(blocks: &[sipx_rtp::ReportBlock], ssrc: u32) -> Option<sipx_rtp::ReportBlock> {
3936    blocks.iter().find(|block| block.ssrc == ssrc).copied()
3937}
3938
3939async fn note_quality(
3940    reporter_ssrc: u32,
3941    block: Option<sipx_rtp::ReportBlock>,
3942    feedback: &Arc<Mutex<Feedback>>,
3943    quality_hook: &QualityHookSlot,
3944    clock_rate: u32,
3945) {
3946    let Some(block) = block else {
3947        return;
3948    };
3949    let now = sipx_rtp::quality::middle_32(sipx_rtp::quality::ntp_now());
3950    let round_trip = sipx_rtp::quality::round_trip(
3951        now,
3952        block.last_sender_report,
3953        block.delay_since_last_sender_report,
3954    );
3955    if let Some(trip) = round_trip {
3956        feedback.lock().await.round_trip = Some(trip);
3957    }
3958    let Some(hook) = current_quality_hook(quality_hook) else {
3959        return;
3960    };
3961    let jitter = if clock_rate == 0 {
3962        Duration::ZERO
3963    } else {
3964        Duration::from_secs_f64(f64::from(block.jitter) / f64::from(clock_rate))
3965    };
3966    let sample = RtcpQualitySample {
3967        reporter_ssrc,
3968        stream_ssrc: block.ssrc,
3969        loss: f64::from(block.fraction_lost) / 256.0,
3970        cumulative_lost: block.cumulative_lost,
3971        jitter,
3972        round_trip,
3973    };
3974    if std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| hook.observe(sample))).is_err() {
3975        tracing::warn!(
3976            reporter_ssrc,
3977            stream_ssrc = block.ssrc,
3978            "RTCP quality callback panicked; media reporting continues"
3979        );
3980    }
3981}
3982
3983/// Where a received packet goes.
3984struct Delivery<'a> {
3985    audio: &'a mpsc::Sender<Vec<i16>>,
3986    encoded: &'a mpsc::Sender<Encoded>,
3987    relay: &'a AtomicBool,
3988    discards: &'a DiscardMeters,
3989}
3990
3991async fn deliver(
3992    to: &Delivery<'_>,
3993    decoding: &mut Decoding,
3994    digits: &Keypresses,
3995    dtmf: &mut sipx_rtp::dtmf::Receiver,
3996    config: &Config,
3997    stop: &Stop,
3998    packet: &Packet,
3999) -> bool {
4000    // A telephone event is a keypress, not audio. It goes to the DTMF path and never to the
4001    // audio one — decoding a four-byte event payload as µ-law injects four garbage samples and
4002    // is heard as a click.
4003    if config.dtmf_payload_type == Some(packet.payload_type) {
4004        if let Some(event) = DtmfEvent::decode(&packet.payload)
4005            && let Some(digit) = dtmf.push(packet.timestamp, &event)
4006        {
4007            // The event's own duration, in its own clock units (RFC 4733 §2.2 — the same
4008            // clock the session negotiated for audio), converted to wall-clock time here so
4009            // every consumer of the channel gets a real duration without knowing the clock
4010            // rate itself.
4011            let millis = u64::from(event.duration) * 1000 / u64::from(config.clock_rate.max(1));
4012            // A full channel means the application is not reading digits. Dropping is
4013            // right: a keypress delivered late is worse than one not delivered, since the
4014            // application has already moved on.
4015            if digits
4016                .to
4017                .try_send((digit, Duration::from_millis(millis)))
4018                .is_ok()
4019            {
4020                // Announced only once the digit is on its way to the application, and only when
4021                // it got there (`M-17`). This is the ordering that makes interrupt-on-digit safe
4022                // to build a `gather` on: a playback can only ever be cut short by a keypress
4023                // that the application will go on to read, so the digit that stopped the prompt
4024                // is never the one the prompt swallowed.
4025                digits
4026                    .arrivals
4027                    .send_modify(|count| *count = count.wrapping_add(1));
4028            } else {
4029                to.discards
4030                    .dtmf_delivery_failures
4031                    .fetch_add(1, Ordering::Relaxed);
4032                tracing::debug!(%digit, "dropping a DTMF digit the application queue could not take");
4033            }
4034        }
4035        return true;
4036    }
4037
4038    // Relaying: hand the payload on exactly as it arrived. The bridge on the other side will
4039    // put it on its own wire with its own sequence and timestamp, which is right — the two
4040    // legs are separate RTP streams that happen to carry the same audio.
4041    if to.relay.load(Ordering::SeqCst) {
4042        let encoded = Encoded {
4043            payload_type: packet.payload_type,
4044            payload: packet.payload.clone(),
4045        };
4046        return tokio::select! {
4047            () = stop.wait() => false,
4048            result = to.encoded.send(encoded) => result.is_ok(),
4049        };
4050    }
4051
4052    // The negotiated payload type is the session's codec, whatever number it was given. Every
4053    // other number is looked up among the static types, and one that is neither is dropped
4054    // rather than decoded as the negotiated codec — decoding somebody else's format produces a
4055    // burst of noise, which is worse than a gap.
4056    if packet.payload_type != config.receive_wire_payload_type()
4057        && Codec::from_payload_type(packet.payload_type).is_none()
4058    {
4059        to.discards
4060            .unknown_payload_type
4061            .fetch_add(1, Ordering::Relaxed);
4062        tracing::debug!(
4063            payload_type = packet.payload_type,
4064            "dropping a packet with an unknown payload type"
4065        );
4066        return true;
4067    }
4068
4069    // The stop signal is checked here too. This is the one await that can park indefinitely —
4070    // a full channel means the application has stopped reading — and a task parked here when
4071    // the call is hung up would hold its socket and its port for the life of the process.
4072    let Some(samples) = decoding.decode(&packet.payload) else {
4073        to.discards
4074            .opus_decode_failures
4075            .fetch_add(1, Ordering::Relaxed);
4076        return true;
4077    };
4078
4079    tokio::select! {
4080        () = stop.wait() => false,
4081        result = to.audio.send(samples) => result.is_ok(),
4082    }
4083}
4084
4085#[cfg(test)]
4086#[allow(
4087    clippy::unwrap_used,
4088    clippy::expect_used,
4089    clippy::panic,
4090    clippy::indexing_slicing,
4091    clippy::cast_possible_truncation
4092)]
4093mod tests {
4094    use super::*;
4095
4096    fn any() -> SocketAddr {
4097        "127.0.0.1:0".parse().expect("valid")
4098    }
4099
4100    fn tone(samples: usize) -> Vec<i16> {
4101        (0..samples)
4102            .map(|i| {
4103                let phase = f64::from(u32::try_from(i).unwrap_or(0)) * 0.05;
4104                (phase.sin() * 8000.0).round() as i16
4105            })
4106            .collect()
4107    }
4108
4109    /// RTCP conventionally travels on the RTP port plus one (RFC 3550 §11), so observing
4110    /// it takes a pair of adjacent ports: the OS picks the first, and the second either
4111    /// binds or the pair is retried.
4112    async fn adjacent_ports() -> (UdpSocket, UdpSocket) {
4113        for _ in 0..32 {
4114            let rtp = UdpSocket::bind(any()).await.expect("binds");
4115            let port = rtp.local_addr().expect("addr").port();
4116            let Some(next) = port.checked_add(1) else {
4117                continue;
4118            };
4119            let rtcp_addr: SocketAddr = format!("127.0.0.1:{next}").parse().expect("valid");
4120            if let Ok(rtcp) = UdpSocket::bind(rtcp_addr).await {
4121                return (rtp, rtcp);
4122            }
4123        }
4124        panic!("no adjacent port pair could be bound");
4125    }
4126
4127    /// RFC 5761 mux has one ICE component even though [`MediaPort`] reserved an adjacent control
4128    /// socket for the no-mux fallback. This pins the public gathering entry point rather than the
4129    /// lower gatherer, so a call path cannot accidentally advertise a second receive owner.
4130    #[tokio::test]
4131    async fn mux_mode_gathers_exactly_one_ice_component() {
4132        let port = MediaPort::bind(any()).await.expect("binds adjacent ports");
4133        let credentials = sipx_sdp::ice::Credentials::new("mux1", "muxPassword0123456789AB")
4134            .expect("valid ICE credentials");
4135        let gathering = ice::Gathering::new(credentials, false);
4136
4137        let mux = port
4138            .gather_with_rtcp_mode(&gathering, sipx_sdp::RtcpMode::Mux)
4139            .await;
4140        assert_eq!(mux.candidates().len(), 1);
4141        assert!(
4142            mux.candidates()
4143                .iter()
4144                .all(|candidate| candidate.component == ComponentId::RTP)
4145        );
4146        assert_eq!(mux.default_destination(ComponentId::RTCP), None);
4147    }
4148
4149    /// How long a test here waits for audio it played to arrive before calling it lost (`X-28`).
4150    ///
4151    /// A bound on failure, not a window to measure in. Every clip below is well under a second,
4152    /// so this is more than an order of magnitude past the honest answer, and its only job is to
4153    /// stop a broken pipeline hanging the suite. The `record_until_idle(300ms)` these replaced
4154    /// was a measurement, and under load it measured the machine rather than the audio — see
4155    /// [`MediaSession::record_at_least`].
4156    ///
4157    /// The tests that still use `record_until_idle` do so deliberately: each asserts its
4158    /// recording is *empty*, so the fixed window is a window to look in rather than a deadline
4159    /// to beat, and a loaded machine can only make them pass. Waiting by count for samples that
4160    /// must never arrive would be a ten-second sleep apiece.
4161    const DELIVERY_BOUND: Duration = Duration::from_secs(10);
4162
4163    async fn pair(codec: Codec) -> (MediaSession, MediaSession) {
4164        // Each side needs the other's port, so bind one first and point the second at it.
4165        let placeholder: SocketAddr = "127.0.0.1:1".parse().expect("valid");
4166        let left = MediaSession::start(any(), Config::new(placeholder, codec))
4167            .await
4168            .expect("binds");
4169        let right = MediaSession::start(any(), Config::new(left.local_addr(), codec))
4170            .await
4171            .expect("binds");
4172        (left, right)
4173    }
4174
4175    /// `MediaSession` is the ownership boundary for every socket worker it starts. Returning
4176    /// from shutdown with an empty registry is the happens-before used by callers that report
4177    /// zero post-drain work; no wall-clock grace period stands in for a join.
4178    #[tokio::test]
4179    async fn ordinary_shutdown_joins_every_owned_worker() {
4180        let placeholder: SocketAddr = "127.0.0.1:1".parse().expect("valid");
4181        let session = MediaSession::start(any(), Config::new(placeholder, Codec::Pcmu))
4182            .await
4183            .expect("session starts");
4184
4185        assert!(
4186            session.owned_task_count().await >= 5,
4187            "send, receive, playback and both separate-RTCP workers are owned"
4188        );
4189        session.shutdown().await;
4190        assert_eq!(session.owned_task_count().await, 0);
4191    }
4192
4193    /// A shutdown future can itself be cancelled by an outer lifecycle deadline. The handle it
4194    /// was joining must remain in the session so the owner can retry and still prove reaping.
4195    #[tokio::test]
4196    async fn cancelled_shutdown_keeps_the_in_flight_worker_owned() {
4197        let placeholder: SocketAddr = "127.0.0.1:1".parse().expect("valid");
4198        let session = Arc::new(
4199            MediaSession::start(any(), Config::new(placeholder, Codec::Pcmu))
4200                .await
4201                .expect("session starts"),
4202        );
4203        let (worker_started_tx, worker_started_rx) = tokio::sync::oneshot::channel();
4204        let (release_tx, release_rx) = tokio::sync::oneshot::channel();
4205        session.owners.lock().await.push(tokio::spawn(async move {
4206            let _ = worker_started_tx.send(());
4207            let _ = release_rx.await;
4208        }));
4209        worker_started_rx.await.expect("test worker starts");
4210
4211        let shutdown_session = Arc::clone(&session);
4212        let shutdown = tokio::spawn(async move { shutdown_session.shutdown().await });
4213        tokio::time::timeout(Duration::from_secs(1), async {
4214            loop {
4215                if session.owners.try_lock().is_err() {
4216                    break;
4217                }
4218                tokio::task::yield_now().await;
4219            }
4220        })
4221        .await // bound on failure: ownership-lock acquisition has no timing semantics.
4222        .expect("shutdown begins joining");
4223        shutdown.abort();
4224        let _ = shutdown.await;
4225
4226        assert!(session.owned_task_count().await >= 1);
4227        release_tx.send(()).expect("test worker remains owned");
4228        session.shutdown().await;
4229        assert_eq!(session.owned_task_count().await, 0);
4230    }
4231
4232    /// Reconfiguration installs a new generation before joining the old one. Cancellation in
4233    /// that join must leave the old generation attached to the replacement so retry can finish
4234    /// the same drain and release the shared socket deterministically.
4235    #[tokio::test]
4236    async fn cancelled_reconfigure_retains_the_old_generation_for_retry() {
4237        let placeholder: SocketAddr = "127.0.0.1:1".parse().expect("valid");
4238        let config = Config::new(placeholder, Codec::Pcmu);
4239        let session = Arc::new(Mutex::new(
4240            MediaSession::start(any(), config.clone())
4241                .await
4242                .expect("session starts"),
4243        ));
4244        let (stop_seen_tx, stop_seen_rx) = tokio::sync::oneshot::channel();
4245        let (release_tx, release_rx) = tokio::sync::oneshot::channel();
4246        {
4247            let mut session = session.lock().await;
4248            let old_stop = Arc::clone(&session.stop);
4249            session.owners.get_mut().push(tokio::spawn(async move {
4250                old_stop.wait().await;
4251                let _ = stop_seen_tx.send(());
4252                let _ = release_rx.await;
4253            }));
4254        }
4255
4256        let reconfiguring_session = Arc::clone(&session);
4257        let retry_config = config.clone();
4258        let reconfiguring = tokio::spawn(async move {
4259            reconfiguring_session
4260                .lock()
4261                .await
4262                .reconfigure(retry_config)
4263                .await
4264        });
4265        tokio::time::timeout(Duration::from_secs(1), stop_seen_rx)
4266            .await // bound on failure: the old generation's stop signal has no timing semantics.
4267            .expect("old generation observes stop")
4268            .expect("stop observer remains alive");
4269        reconfiguring.abort();
4270        let _ = reconfiguring.await;
4271
4272        let local_addr = {
4273            let mut session = session.lock().await;
4274            assert_eq!(
4275                session.retired.get_mut().len(),
4276                1,
4277                "the cancelled join retained its old generation"
4278            );
4279            release_tx.send(()).expect("old generation remains owned");
4280            assert!(session.reconfigure(config).await.expect("retry succeeds"));
4281            assert_eq!(session.retired.get_mut().len(), 0);
4282            session.shutdown().await;
4283            session.local_addr()
4284        };
4285        let session = Arc::try_unwrap(session)
4286            .expect("the test owns the replacement")
4287            .into_inner();
4288        drop(session);
4289        let rebound = UdpSocket::bind(local_addr)
4290            .await
4291            .expect("retry joined every old socket worker");
4292        drop(rebound);
4293    }
4294
4295    /// The failing-first test for this story.
4296    #[tokio::test]
4297    async fn audio_played_into_a_session_arrives_at_the_far_end() {
4298        let (left, right) = pair(Codec::Pcmu).await;
4299        let source = tone(1600); // 200 ms
4300
4301        // `right` speaks; `left` listens. `left` does not know `right`'s address until a
4302        // packet arrives, which is exactly what symmetric RTP is for.
4303        right.play(&source, 160).await;
4304        let recorded = left.record_at_least(source.len(), DELIVERY_BOUND).await;
4305
4306        assert_eq!(recorded.len(), source.len(), "every packet arrived");
4307
4308        // G.711 is lossy, so the samples cannot be compared directly — but the codec is
4309        // idempotent, so encoding the source and encoding what came back must agree exactly.
4310        assert_eq!(
4311            g711::ulaw_encode_all(&source),
4312            g711::ulaw_encode_all(&recorded),
4313            "the audio that arrived is the audio that was sent"
4314        );
4315    }
4316
4317    /// M-43 / `linear-pcm.md` §3: playback converts both depth and rate before codec encoding.
4318    /// The decoded output is asserted so an implementation that merely accepts the format cannot
4319    /// pass while still putting byte-depth garbage on the wire.
4320    #[tokio::test]
4321    async fn pcm_playback_converts_unsigned_eight_and_signed_sixteen_bit_sources() {
4322        let (left, right) = pair(Codec::Pcmu).await;
4323        let eight = sipx_audio::Pcm::new(
4324            sipx_audio::PcmFormat::new(8_000, sipx_audio::PcmEncoding::Unsigned8).expect("format"),
4325            sipx_audio::PcmSamples::Unsigned8(vec![128; 160]),
4326        )
4327        .expect("samples");
4328        assert!(right.play_pcm(&eight).await.expect("converts"));
4329        let silent = left.record_at_least(160, DELIVERY_BOUND).await;
4330        assert_eq!(silent.len(), 160);
4331        assert!(silent.iter().all(|sample| sample.abs() <= 4));
4332
4333        let source = tone(320);
4334        let sixteen = sipx_audio::Pcm::new(
4335            sipx_audio::PcmFormat::new(16_000, sipx_audio::PcmEncoding::Signed16).expect("format"),
4336            sipx_audio::PcmSamples::Signed16(source),
4337        )
4338        .expect("samples");
4339        assert!(right.play_pcm(&sixteen).await.expect("resamples"));
4340        let downsampled = left.record_at_least(160, DELIVERY_BOUND).await;
4341        assert_eq!(
4342            downsampled.len(),
4343            160,
4344            "16 kHz becomes the same duration at 8 kHz"
4345        );
4346        assert!(downsampled.iter().any(|sample| sample.abs() > 1_000));
4347    }
4348
4349    /// M-43 / `linear-pcm.md` §3: capture owns a continuous resampler and emits the caller's
4350    /// chosen rate instead of exposing the negotiated codec clock as an application assumption.
4351    #[tokio::test]
4352    async fn pcm_capture_resamples_received_audio_to_the_callers_rate() {
4353        let (left, right) = pair(Codec::Pcmu).await;
4354        let source = tone(320);
4355        let format =
4356            sipx_audio::PcmFormat::new(16_000, sipx_audio::PcmEncoding::Signed16).expect("format");
4357        let mut capture = left.capture(format).expect("capture format");
4358        right.play(&source, 160).await;
4359        let pcm = capture.record_at_least(639, DELIVERY_BOUND).await;
4360        assert_eq!(pcm.format(), format);
4361        assert_eq!(pcm.samples().len(), 639);
4362    }
4363
4364    /// Mute, from the receiving side (`M-18`): the far end gets every packet it would have got,
4365    /// and decodes silence out of all of them. Asserting the count as well as the content is the
4366    /// point — it is what distinguishes the decision that was made from the one that was not.
4367    #[tokio::test]
4368    async fn a_muted_session_sends_silence_rather_than_stopping() {
4369        let (left, right) = pair(Codec::Pcmu).await;
4370        let source = tone(800); // five packets
4371
4372        right.set_muted(true);
4373        right.play(&source, 160).await;
4374        let recorded = left.record_at_least(source.len(), DELIVERY_BOUND).await;
4375
4376        assert_eq!(recorded.len(), source.len(), "the stream did not stop");
4377        assert!(
4378            recorded.iter().all(|sample| *sample == 0),
4379            "a muted session put audio on the wire"
4380        );
4381        // RFC 3550 §6.4.1: what we say we sent is what arrived, and the far end saw no gap in the
4382        // sequence space to score as loss.
4383        assert_eq!(right.packets_sent(), 5);
4384        assert_eq!(left.packets_received(), 5);
4385        assert_eq!(left.quality().await.cumulative_lost, 0);
4386    }
4387
4388    /// The gate opens again, on the same session — no renegotiation is involved at this layer or
4389    /// any other.
4390    #[tokio::test]
4391    async fn unmuting_a_session_restores_the_audio() {
4392        let (left, right) = pair(Codec::Pcmu).await;
4393        let source = tone(480);
4394
4395        right.set_muted(true);
4396        right.play(&source, 160).await;
4397        // Drained by count, not by an idle window, and that matters more here than anywhere
4398        // else in this module (`X-28`): a short first recording leaves the rest of the muted
4399        // silence in the channel, where the second recording picks it up and compares it
4400        // against the source. The failure then reads as "unmuting did not restore the audio",
4401        // which is a lie about the code under test.
4402        let muted = left.record_at_least(source.len(), DELIVERY_BOUND).await;
4403        assert!(muted.iter().all(|sample| *sample == 0));
4404
4405        assert!(right.set_muted(false), "the gate was closed before this");
4406        right.play(&source, 160).await;
4407        let after = left.record_at_least(source.len(), DELIVERY_BOUND).await;
4408
4409        assert_eq!(
4410            g711::ulaw_encode_all(&source),
4411            g711::ulaw_encode_all(&after),
4412            "the audio that arrived after unmuting is the audio that was sent"
4413        );
4414    }
4415
4416    /// A relayed payload is opaque — a bridge's bytes in whatever the other leg negotiated — so a
4417    /// muted session substitutes its own silence for it rather than passing it on.
4418    #[tokio::test]
4419    async fn a_muted_session_does_not_relay_a_payload_either() {
4420        let (left, right) = pair(Codec::Pcmu).await;
4421
4422        right.set_muted(true);
4423        for _ in 0..3 {
4424            assert!(
4425                right
4426                    .send_encoded(Encoded {
4427                        payload_type: 0,
4428                        payload: Bytes::from(g711::ulaw_encode_all(&tone(160))),
4429                    })
4430                    .await
4431            );
4432        }
4433        let recorded = left.record_at_least(480, DELIVERY_BOUND).await;
4434
4435        assert_eq!(recorded.len(), 480, "the stream did not stop");
4436        assert!(
4437            recorded.iter().all(|sample| *sample == 0),
4438            "a muted session forwarded somebody else's audio"
4439        );
4440    }
4441
4442    /// A keypress is not audio. It is generated by this endpoint on purpose, and a mute that
4443    /// swallowed it would leave a muted caller unable to answer an IVR.
4444    #[tokio::test]
4445    async fn a_muted_session_still_sends_a_keypress() {
4446        let (left, right) = pair(Codec::Pcmu).await;
4447        right.play(&tone(320), 160).await;
4448        let _ = left.record_at_least(320, DELIVERY_BOUND).await;
4449
4450        right.set_muted(true);
4451        right
4452            .send_digit(
4453                Digit::from_char('9').expect("a digit"),
4454                Duration::from_millis(100),
4455            )
4456            .await;
4457
4458        let (digit, _duration) = tokio::time::timeout(Duration::from_secs(2), left.recv_digit())
4459            .await
4460            .expect("no timeout")
4461            .expect("a digit arrives");
4462        assert_eq!(digit.as_char(), '9');
4463    }
4464
4465    /// Reception is not part of the gate: a muted session hears everything it would have heard.
4466    #[tokio::test]
4467    async fn a_muted_session_still_receives() {
4468        let (left, right) = pair(Codec::Pcmu).await;
4469        let source = tone(480);
4470
4471        left.set_muted(true);
4472        right.play(&source, 160).await;
4473        let recorded = left.record_at_least(source.len(), DELIVERY_BOUND).await;
4474
4475        assert_eq!(
4476            g711::ulaw_encode_all(&source),
4477            g711::ulaw_encode_all(&recorded),
4478            "muting this side must not touch what it hears"
4479        );
4480    }
4481
4482    /// A queue has to say what it does when it is full. Refusing, with the handle resolving to
4483    /// say so, beats the two alternatives: silently dropping the clip leaves the caller waiting
4484    /// on audio that is never coming, and waiting for room parks a call's control path on a
4485    /// backlog it may never work through.
4486    #[tokio::test]
4487    async fn a_full_playback_queue_refuses_rather_than_dropping_or_waiting() {
4488        let (left, _right) = pair(Codec::Pcmu).await;
4489
4490        // One clip reaches the head of the queue and starts feeding, so the queue proper only
4491        // has room for `Playback::QUEUE_DEPTH` behind it.
4492        let mut started = Vec::new();
4493        for _ in 0..=Playback::QUEUE_DEPTH + 1 {
4494            started.push(left.start_playback(tone(160 * 200), Interrupt::Never));
4495        }
4496
4497        let refused = started
4498            .iter()
4499            .filter(|playback| playback.end() == Some(PlaybackEnd::Refused))
4500            .count();
4501        assert!(
4502            refused > 0,
4503            "a queue {} deep must refuse the clip past its depth rather than \
4504             growing without bound",
4505            Playback::QUEUE_DEPTH
4506        );
4507        assert!(
4508            started
4509                .first()
4510                .is_some_and(|playback| playback.end().is_none()),
4511            "and it must refuse the newest clip, not the one already playing"
4512        );
4513    }
4514
4515    /// A playback started on a session that has already stopped resolves at once rather than
4516    /// hanging. The `Call` this reports through has no other way to learn it will never play.
4517    #[tokio::test]
4518    async fn a_playback_started_on_a_stopped_session_resolves_at_once() {
4519        let (left, _right) = pair(Codec::Pcmu).await;
4520        left.stop();
4521        // The playback task takes the stop signal on its next poll; until then a clip is accepted
4522        // and then resolved by the task itself.
4523        let playback = left.start_playback(tone(320), Interrupt::Never);
4524        let end = tokio::time::timeout(Duration::from_secs(2), playback.finished())
4525            .await
4526            .expect("a stopped session must not leave a playback hanging");
4527        assert_eq!(end, PlaybackEnd::SessionEnded);
4528    }
4529
4530    /// `play` is cancel-on-drop, and has to stay that way: callers wrap it in a `timeout` to cap
4531    /// how long a clip may run, and feeding the clip from a task of its own would have quietly
4532    /// turned that into a timeout that returns while the audio plays on.
4533    #[tokio::test]
4534    async fn abandoning_a_play_stops_the_clip_rather_than_leaving_it_running() {
4535        let (left, right) = pair(Codec::Pcmu).await;
4536        // Far longer than the timeout, so a clip that survives it is unmistakable.
4537        let long = tone(160 * 250);
4538
4539        let capped = tokio::time::timeout(Duration::from_millis(60), left.play(&long, 160)).await;
4540        assert!(capped.is_err(), "the timeout is what ends this play");
4541
4542        let before = left.packets_sent();
4543        // A definition of silence: how long a hole has to be before "the clip stopped" is true.
4544        // Both assertions are negative — no more than the bound went out, and the far end did not
4545        // hear the whole clip — so load lengthens the window and can only make them fail, and
4546        // there is no arrival to poll for (`X-44`).
4547        tokio::time::sleep(Duration::from_millis(400)).await;
4548        assert!(
4549            left.packets_sent() - before <= Playback::STOP_BOUND_PACKETS,
4550            "abandoning the play must stop the clip, not leave it going"
4551        );
4552        assert!(
4553            right.packets_received() < 250,
4554            "the far end heard the whole clip despite the timeout"
4555        );
4556    }
4557
4558    /// A frame that belongs to no playback is never discarded — [`MediaSession::send`] is the
4559    /// path a bridge and a conference mixer use, and nothing there has a handle to stop.
4560    #[test]
4561    fn only_a_stopped_playback_s_frames_are_discarded() {
4562        let stop = Arc::new(Stop::default());
4563        let untagged = Frame::Audio {
4564            samples: vec![0; 160],
4565            playback: None,
4566        };
4567        let tagged = Frame::Audio {
4568            samples: vec![0; 160],
4569            playback: Some(Arc::clone(&stop)),
4570        };
4571        assert!(!discarded(&untagged));
4572        assert!(!discarded(&tagged));
4573        stop.stop();
4574        assert!(discarded(&tagged));
4575        assert!(!discarded(&untagged));
4576    }
4577
4578    #[tokio::test]
4579    async fn audio_flows_in_both_directions_at_once() {
4580        let (left, right) = pair(Codec::Pcmu).await;
4581        let from_left = tone(800);
4582        let from_right: Vec<i16> = tone(800).iter().map(|s| -s).collect();
4583
4584        // Left must learn right's address, which it does from right's first packet — so wait for
4585        // that packet to have *arrived* rather than for a window to pass (`X-44`). The primer is
4586        // one packet's worth, and recording it here is also what makes the count below exact:
4587        // what `left` hears afterwards is everything except this primer.
4588        right.play(&from_right[..160], 160).await;
4589        assert_eq!(
4590            left.record_at_least(160, DELIVERY_BOUND).await.len(),
4591            160,
4592            "left never heard right's primer, so it cannot have learned right's address"
4593        );
4594
4595        let (left_recorded, right_recorded) = tokio::join!(
4596            async {
4597                left.play(&from_left, 160).await;
4598                // What `left` hears is what `right` still has to play: everything after the
4599                // 160-sample primer above.
4600                left.record_at_least(from_right.len() - 160, DELIVERY_BOUND)
4601                    .await
4602            },
4603            async {
4604                right.play(&from_right[160..], 160).await;
4605                right.record_at_least(from_left.len(), DELIVERY_BOUND).await
4606            }
4607        );
4608
4609        assert!(!left_recorded.is_empty(), "left heard nothing");
4610        assert!(!right_recorded.is_empty(), "right heard nothing");
4611    }
4612
4613    #[tokio::test]
4614    async fn a_pcma_session_carries_a_law() {
4615        let (left, right) = pair(Codec::Pcma).await;
4616        let source = tone(480);
4617        right.play(&source, 160).await;
4618        let recorded = left.record_at_least(source.len(), DELIVERY_BOUND).await;
4619        assert_eq!(
4620            g711::alaw_encode_all(&source),
4621            g711::alaw_encode_all(&recorded)
4622        );
4623    }
4624
4625    /// M-43: dynamic L16 uses the negotiated clock and payload assignment, while the encoded
4626    /// samples remain signed network-order PCM and therefore arrive bit-for-bit unchanged.
4627    #[tokio::test]
4628    async fn a_dynamic_eight_kilohertz_l16_session_carries_linear_pcm() {
4629        let placeholder: SocketAddr = "127.0.0.1:1".parse().expect("valid");
4630        let mut left_config = Config::new(placeholder, Codec::L16);
4631        left_config.clock_rate = 8_000;
4632        left_config.payload_type = Some(96);
4633        left_config.receive_payload_type = Some(96);
4634        let left = MediaSession::start(any(), left_config)
4635            .await
4636            .expect("binds");
4637
4638        let mut right_config = Config::new(left.local_addr(), Codec::L16);
4639        right_config.clock_rate = 8_000;
4640        right_config.payload_type = Some(96);
4641        right_config.receive_payload_type = Some(96);
4642        let right = MediaSession::start(any(), right_config)
4643            .await
4644            .expect("binds");
4645
4646        let source = tone(480);
4647        right.play(&source, 160).await;
4648        let recorded = left.record_at_least(source.len(), DELIVERY_BOUND).await;
4649        assert_eq!(recorded, source);
4650        assert_eq!(left.clock_rate(), 8_000);
4651        assert_eq!(left.wire_payload_type(), 96);
4652    }
4653
4654    /// Media ports attract stray traffic — STUN probes, port scans. None of it should end a
4655    /// call.
4656    #[tokio::test]
4657    async fn junk_on_the_media_port_does_not_stop_the_session() {
4658        let (left, right) = pair(Codec::Pcmu).await;
4659
4660        let junk = UdpSocket::bind(any()).await.expect("binds");
4661        for _ in 0..5 {
4662            junk.send_to(b"not an RTP packet", left.local_addr())
4663                .await
4664                .expect("sends");
4665        }
4666
4667        let source = tone(320);
4668        right.play(&source, 160).await;
4669        let recorded = left.record_at_least(source.len(), DELIVERY_BOUND).await;
4670        assert_eq!(
4671            g711::ulaw_encode_all(&source),
4672            g711::ulaw_encode_all(&recorded),
4673            "the session survived the junk"
4674        );
4675    }
4676
4677    /// Symmetric RTP: `left` was configured with a useless address and still answers, because
4678    /// the observed source replaced it.
4679    #[tokio::test]
4680    async fn media_returns_to_where_it_came_from_not_where_the_sdp_said() {
4681        let (left, right) = pair(Codec::Pcmu).await;
4682
4683        // Left latches the source address off right's first packet, so wait for that packet to
4684        // have arrived rather than for a window to pass (`X-44`). A fixed window here was racing
4685        // the same pipeline `X-28` measured — two 20 ms pacers and a jitter buffer entitled to
4686        // grow — and losing it produced a reply sent to 127.0.0.1:1 and an empty recording.
4687        right.play(&tone(320), 160).await;
4688        assert_eq!(
4689            left.record_at_least(320, DELIVERY_BOUND).await.len(),
4690            320,
4691            "left never heard right, so it cannot have latched right's address"
4692        );
4693
4694        let reply = tone(320);
4695        left.play(&reply, 160).await;
4696        let heard = right.record_at_least(reply.len(), DELIVERY_BOUND).await;
4697
4698        assert!(
4699            !heard.is_empty(),
4700            "left was configured with 127.0.0.1:1 and must have learned the real address"
4701        );
4702    }
4703
4704    /// ICE and symmetric RTP must not race to write the same destination. `on_socket` supplies
4705    /// `symmetric = false` exactly when an ICE driver owns the session; a valid ordinary RTP
4706    /// packet may establish the SSRC, but cannot replace the nominated pair.
4707    #[tokio::test]
4708    async fn an_ice_owned_destination_cannot_be_replaced_by_ordinary_rtp() {
4709        let nominated: SocketAddr = "127.0.0.1:40000".parse().expect("valid");
4710        let unsolicited: SocketAddr = "127.0.0.1:50000".parse().expect("valid");
4711        let remote = Arc::new(Mutex::new(nominated));
4712        let stats = Arc::new(Mutex::new(StreamStats::new(1)));
4713        let discards = DiscardMeters::default();
4714        let packet = Packet::new(0, 1, 160, 7, Bytes::from(vec![0xff; 160]));
4715        let mut stream = None;
4716
4717        assert!(
4718            accept_source(
4719                &mut stream,
4720                &packet,
4721                unsolicited,
4722                &remote,
4723                &stats,
4724                false,
4725                &discards,
4726            )
4727            .await,
4728            "the nominated path may still receive the stream"
4729        );
4730        assert_eq!(
4731            *remote.lock().await,
4732            nominated,
4733            "RFC 8445 nomination, not the first ordinary RTP source, owns the destination"
4734        );
4735    }
4736
4737    #[tokio::test]
4738    async fn packets_are_counted_on_both_sides() {
4739        let (left, right) = pair(Codec::Pcmu).await;
4740        right.play(&tone(800), 160).await;
4741        // Counted, not timed (`X-28`). The recording is discarded, but waiting for it is what
4742        // gives all five packets time to land — so an idle window that closed early made
4743        // `packets_received` short and blamed the counters.
4744        let _ = left.record_at_least(800, DELIVERY_BOUND).await;
4745
4746        assert_eq!(right.packets_sent(), 5);
4747        assert_eq!(left.packets_received(), 5);
4748    }
4749
4750    /// A short final chunk is padded so every packet is the same size, which is what a far-end
4751    /// jitter buffer expects.
4752    #[tokio::test]
4753    async fn a_partial_final_frame_is_padded_rather_than_sent_short() {
4754        let (left, right) = pair(Codec::Pcmu).await;
4755        right.play(&tone(400), 160).await; // 2.5 packets
4756        let recorded = left.record_at_least(480, DELIVERY_BOUND).await;
4757        assert_eq!(recorded.len(), 480, "three whole packets");
4758        assert_eq!(&recorded[400..], &[0i16; 80], "padded with silence");
4759    }
4760
4761    /// The acceptance test for M-7: a keypress crosses a real media session and arrives once.
4762    #[tokio::test]
4763    async fn a_dtmf_digit_survives_a_media_session() {
4764        let (left, right) = pair(Codec::Pcmu).await;
4765
4766        // Establish the stream so `left` knows where `right` is.
4767        right.play(&tone(320), 160).await;
4768        assert_eq!(left.record_at_least(320, DELIVERY_BOUND).await.len(), 320);
4769
4770        right
4771            .send_digit(
4772                Digit::from_char('5').expect("a digit"),
4773                Duration::from_millis(100),
4774            )
4775            .await;
4776
4777        let (digit, duration) = tokio::time::timeout(Duration::from_secs(2), left.recv_digit())
4778            .await
4779            .expect("no timeout")
4780            .expect("a digit arrives");
4781        assert_eq!(digit.as_char(), '5');
4782        assert!(
4783            duration >= Duration::from_millis(80) && duration <= Duration::from_millis(140),
4784            "the reported duration must reflect how long the digit was held: {duration:?}"
4785        );
4786
4787        // Exactly once, however many packets carried it.
4788        assert!(
4789            tokio::time::timeout(Duration::from_millis(300), left.recv_digit())
4790                .await
4791                .is_err(),
4792            "one keypress must not be reported twice"
4793        );
4794    }
4795
4796    /// A whole sequence, as an application collecting a PIN would see it.
4797    #[tokio::test]
4798    async fn a_sequence_of_keypresses_arrives_in_order() {
4799        let (left, right) = pair(Codec::Pcmu).await;
4800        right.play(&tone(160), 160).await;
4801        let _ = left.record_at_least(160, DELIVERY_BOUND).await;
4802
4803        for c in "1234".chars() {
4804            right
4805                .send_digit(
4806                    Digit::from_char(c).expect("a digit"),
4807                    Duration::from_millis(80),
4808                )
4809                .await;
4810        }
4811
4812        let collected = left.collect_digits(FIRST_DIGIT_BOUND, DIGIT_GAP).await;
4813        assert_eq!(collected, "1234");
4814    }
4815
4816    /// How long a collection here waits for the **first** digit before calling it lost (`M-34`).
4817    ///
4818    /// A bound on failure, like [`DELIVERY_BOUND`], and for the same reason: how long a caller
4819    /// takes to press the first key is a property of the caller and of the machine carrying the
4820    /// call, never of the digits.
4821    const FIRST_DIGIT_BOUND: Duration = Duration::from_secs(10);
4822
4823    /// How long a silence means the caller has stopped dialling, for the tests here (`M-34`).
4824    ///
4825    /// A definition of silence, so it is set past any scheduling delay rather than close to the
4826    /// spacing the digits actually arrive with — fifty missed packet intervals, which is `X-28`'s
4827    /// treatment of the windows that genuinely have to stay wall-clock.
4828    const DIGIT_GAP: Duration = Duration::from_secs(1);
4829
4830    /// A first digit the caller is slow to press is still collected (`M-34`).
4831    ///
4832    /// The defect this pins: `collect_digits` spent one window on both "how long to wait for the
4833    /// first digit" and "how long a gap means the digits ended", so a caller who took longer than
4834    /// that window to press anything collected **nothing at all** — not a short sequence, an empty
4835    /// one, because the loop ended before its first iteration. That is the same one-window shape
4836    /// that made `sipx answer` write a valid WAV with zero samples (`X-40`), one layer down, and
4837    /// the reproduction is the same: delay the first thing the far end sends.
4838    #[tokio::test]
4839    async fn a_first_digit_that_arrives_late_is_still_collected() {
4840        let (left, right) = pair(Codec::Pcmu).await;
4841
4842        // Establish the stream first, so the only variable below is *when* the digits start.
4843        right.play(&tone(160), 160).await;
4844        let _ = left.record_at_least(160, DELIVERY_BOUND).await;
4845
4846        // Longer than the gap, so a collection that spends its gap on the first digit has already
4847        // given up by the time the caller presses anything. Load can only push the digits later,
4848        // which makes the pre-split failure more certain rather than less.
4849        let late = Duration::from_secs(2);
4850
4851        let (collected, ()) =
4852            tokio::join!(left.collect_digits(FIRST_DIGIT_BOUND, DIGIT_GAP), async {
4853                tokio::time::sleep(late).await;
4854                for c in "1234".chars() {
4855                    right
4856                        .send_digit(
4857                            Digit::from_char(c).expect("a digit"),
4858                            Duration::from_millis(80),
4859                        )
4860                        .await;
4861                }
4862            });
4863
4864        assert_eq!(
4865            collected, "1234",
4866            "a caller slow to press the first key has not finished dialling"
4867        );
4868    }
4869
4870    /// Digits that never arrive still end the collection, and end it empty (`M-34`).
4871    ///
4872    /// The other half of the split: separating the two bounds must not turn "nobody pressed
4873    /// anything" into a wait that never ends, and it must not invent digits to return.
4874    #[tokio::test]
4875    async fn a_collection_with_no_digits_at_all_ends_empty() {
4876        let (left, right) = pair(Codec::Pcmu).await;
4877
4878        right.play(&tone(160), 160).await;
4879        let _ = left.record_at_least(160, DELIVERY_BOUND).await;
4880
4881        let collected = tokio::time::timeout(
4882            DELIVERY_BOUND,
4883            left.collect_digits(Duration::from_millis(300), Duration::from_millis(300)),
4884        )
4885        .await
4886        .expect("the collection is bounded when no digit ever arrives");
4887
4888        assert_eq!(collected, "", "audio alone is not a keypress");
4889    }
4890
4891    /// DTMF must not become audio and audio must not become digits.
4892    #[tokio::test]
4893    async fn keypresses_and_audio_stay_on_their_own_paths() {
4894        let (left, right) = pair(Codec::Pcmu).await;
4895
4896        right.play(&tone(320), 160).await;
4897        let audio = left.record_at_least(320, DELIVERY_BOUND).await;
4898        assert_eq!(audio.len(), 320, "audio arrived");
4899        assert!(
4900            tokio::time::timeout(Duration::from_millis(100), left.recv_digit())
4901                .await
4902                .is_err(),
4903            "audio must not be reported as a keypress"
4904        );
4905
4906        right
4907            .send_digit(
4908                Digit::from_char('#').expect("a digit"),
4909                Duration::from_millis(80),
4910            )
4911            .await;
4912        let (digit, _duration) = tokio::time::timeout(Duration::from_secs(2), left.recv_digit())
4913            .await
4914            .expect("no timeout")
4915            .expect("a digit");
4916        assert_eq!(digit.as_char(), '#');
4917
4918        let after = left.record_until_idle(Duration::from_millis(200)).await;
4919        assert!(
4920            after.is_empty(),
4921            "a keypress must not become audio samples: {after:?}"
4922        );
4923    }
4924
4925    /// With nothing negotiated for `telephone-event`, a digit cannot be sent — and guessing a
4926    /// payload type would put keypresses on whatever the far end uses that number for.
4927    #[tokio::test]
4928    async fn a_digit_is_not_sent_when_no_payload_type_was_negotiated() {
4929        let listener = UdpSocket::bind(any()).await.expect("binds");
4930        let mut config = Config::new(listener.local_addr().expect("addr"), Codec::Pcmu);
4931        config.dtmf_payload_type = None;
4932        let session = MediaSession::start(any(), config).await.expect("binds");
4933
4934        session
4935            .send_digit(
4936                Digit::from_char('7').expect("a digit"),
4937                Duration::from_millis(80),
4938            )
4939            .await;
4940
4941        let mut datagram = vec![0u8; 2048];
4942        assert!(
4943            tokio::time::timeout(
4944                Duration::from_millis(300),
4945                listener.recv_from(&mut datagram)
4946            )
4947            .await
4948            .is_err(),
4949            "nothing should go on the wire"
4950        );
4951    }
4952
4953    /// Statistics are readable mid-call, and count what the receive path actually saw.
4954    /// Numbers that only appear when a call ends cannot be used to do anything about it.
4955    #[tokio::test]
4956    async fn a_session_reports_the_loss_it_saw() {
4957        let (left, right) = pair(Codec::Pcmu).await;
4958
4959        // Ten packets from `right`, of which two are dropped in flight by sending them from a
4960        // socket the far end will ignore — simpler: send nine of ten sequence numbers by
4961        // hand, so the gap is exact.
4962        let raw = UdpSocket::bind(any()).await.expect("binds");
4963        for sequence in 1u16..=10 {
4964            if sequence == 4 || sequence == 8 {
4965                continue;
4966            }
4967            let packet = Packet::new(
4968                0,
4969                sequence,
4970                u32::from(sequence) * 160,
4971                0xAB,
4972                Bytes::from(vec![0xFFu8; 160]),
4973            );
4974            raw.send_to(&packet.encode(), left.local_addr())
4975                .await
4976                .expect("sends");
4977            tokio::time::sleep(Duration::from_millis(5)).await;
4978        }
4979        // Wait for all eight to have been through the receive path, rather than sleeping 150 ms
4980        // and assuming they have (`X-29`). The counts below are exact, so a packet still in
4981        // flight does not degrade the answer — it changes it, and reports loss that was never
4982        // injected. The bound is on failure, not a window to measure in.
4983        //
4984        // The precondition this leans on, since it is not obvious: `packets_received()` reaching 8
4985        // implies the statistics have seen all 8 only because nothing suspends between
4986        // `received.fetch_add` (`:2258`) and `note_arrival`'s lock (`:2301-2313`) — an uncontended
4987        // `Mutex::lock().await` on a `current_thread` runtime does not yield. Move these tests to a
4988        // multi-thread runtime, or add an await in that gap, and the counter can lead the
4989        // statistics: inserting a 20 ms sleep between the two fails this test with
4990        // `extended_highest_sequence  left: 9  right: 10`.
4991        let deadline = tokio::time::Instant::now() + Duration::from_secs(10);
4992        while left.packets_received() != 8 {
4993            assert!(
4994                tokio::time::Instant::now() < deadline,
4995                "the eight hand-sent packets never reached the receive path"
4996            );
4997            tokio::time::sleep(Duration::from_millis(5)).await;
4998        }
4999
5000        let block = left.stats().await;
5001        assert_eq!(
5002            block.extended_highest_sequence, 10,
5003            "sequence 10 was the highest"
5004        );
5005        assert_eq!(block.cumulative_lost, 2, "four and eight never arrived");
5006        assert!(block.fraction_lost > 0, "and the interval shows loss");
5007
5008        drop(right);
5009    }
5010
5011    #[test]
5012    fn the_packet_size_follows_the_clock_rate_and_interval() {
5013        let config = Config::new("127.0.0.1:1".parse().expect("valid"), Codec::Pcmu);
5014        assert_eq!(config.samples_per_packet(), 160, "8 kHz at 20 ms");
5015
5016        let mut ten_ms = config.clone();
5017        ten_ms.packet_duration = Duration::from_millis(10);
5018        assert_eq!(ten_ms.samples_per_packet(), 80);
5019    }
5020
5021    #[tokio::test]
5022    async fn zero_packet_duration_is_rejected_before_binding_or_spawning() {
5023        let reservation = UdpSocket::bind(any()).await.expect("reserves a port");
5024        let address = reservation.local_addr().expect("has an address");
5025        drop(reservation);
5026
5027        let mut config = Config::new("127.0.0.1:9".parse().expect("valid"), Codec::Pcmu);
5028        config.packet_duration = Duration::ZERO;
5029        let error = MediaSession::start(address, config)
5030            .await
5031            .expect_err("zero cannot pace a worker");
5032        assert!(matches!(
5033            error,
5034            StartError::Setup(SetupError::PacketDurationTooShort(Duration::ZERO))
5035        ));
5036
5037        let rebound = UdpSocket::bind(address)
5038            .await
5039            .expect("rejected setup left no socket behind");
5040        drop(rebound);
5041    }
5042
5043    #[tokio::test]
5044    async fn zero_rtcp_interval_is_rejected_before_binding_or_spawning() {
5045        let reservation = UdpSocket::bind(any()).await.expect("reserves a port");
5046        let address = reservation.local_addr().expect("has an address");
5047        drop(reservation);
5048
5049        let mut config = Config::new("127.0.0.1:9".parse().expect("valid"), Codec::Pcmu);
5050        config.rtcp_interval = Some(Duration::ZERO);
5051        let error = MediaSession::start(address, config)
5052            .await
5053            .expect_err("zero cannot schedule reports");
5054        assert!(matches!(
5055            error,
5056            StartError::Setup(SetupError::RtcpIntervalTooShort(Duration::ZERO))
5057        ));
5058
5059        let rebound = UdpSocket::bind(address)
5060            .await
5061            .expect("rejected setup left no socket behind");
5062        drop(rebound);
5063    }
5064
5065    #[test]
5066    fn rtcp_mux_refuses_payload_types_that_collide_with_rtcp() {
5067        let mut config = Config::new(any(), Codec::Pcmu);
5068        config.rtcp_mode = sipx_sdp::RtcpMode::Mux;
5069        config.payload_type = Some(72);
5070        assert!(matches!(
5071            config.validate(),
5072            Err(SetupError::RtcpMuxPayloadCollision(72))
5073        ));
5074
5075        config.payload_type = Some(0);
5076        config.dtmf_payload_type = Some(95);
5077        assert!(matches!(
5078            config.validate(),
5079            Err(SetupError::RtcpMuxPayloadCollision(95))
5080        ));
5081    }
5082
5083    #[tokio::test]
5084    async fn one_millisecond_media_and_report_intervals_keep_running() {
5085        let peer = UdpSocket::bind(any()).await.expect("binds peer");
5086        let mut config = Config::new(peer.local_addr().expect("has an address"), Codec::Pcmu);
5087        config.packet_duration = Duration::from_millis(1);
5088        config.rtcp_interval = Some(Duration::from_millis(1));
5089        let samples = config.samples_per_packet();
5090        let session = MediaSession::start(any(), config)
5091            .await
5092            .expect("minimum intervals are valid");
5093
5094        assert!(session.send(vec![0; samples]).await);
5095        let mut datagram = vec![0u8; 2048];
5096        tokio::time::timeout(Duration::from_secs(1), peer.recv_from(&mut datagram))
5097            .await
5098            .expect("the pacing worker remains alive")
5099            .expect("receives a packet");
5100        session.stop();
5101    }
5102
5103    #[cfg(feature = "opus")]
5104    #[test]
5105    fn refused_opus_encoder_has_no_fallback_pipeline() {
5106        let error =
5107            Encoding::for_codec(Codec::Opus, 3).expect_err("Opus carries at most two channels");
5108        assert!(matches!(
5109            error,
5110            SetupError::Codec {
5111                codec: Codec::Opus,
5112                direction: CodecDirection::Encoder,
5113                ..
5114            }
5115        ));
5116    }
5117
5118    #[cfg(feature = "opus")]
5119    #[test]
5120    fn refused_opus_decoder_has_no_fallback_pipeline() {
5121        let error =
5122            Decoding::for_codec(Codec::Opus, 3).expect_err("Opus carries at most two channels");
5123        assert!(matches!(
5124            error,
5125            SetupError::Codec {
5126                codec: Codec::Opus,
5127                direction: CodecDirection::Decoder,
5128                ..
5129            }
5130        ));
5131    }
5132
5133    /// The RTP timestamp must advance by the samples actually sent. Advancing by the
5134    /// configured packet size instead builds a timeline at the wrong rate, and the far end
5135    /// plays the call with a gap between every packet.
5136    #[tokio::test]
5137    async fn the_timestamp_follows_the_frame_actually_sent() {
5138        let listener = UdpSocket::bind(any()).await.expect("binds");
5139        let listen_addr = listener.local_addr().expect("has an address");
5140        let session = MediaSession::start(any(), Config::new(listen_addr, Codec::Pcmu))
5141            .await
5142            .expect("binds");
5143
5144        // Half-sized frames on a config that says 160.
5145        for _ in 0..3 {
5146            session.send(vec![0i16; 80]).await;
5147        }
5148
5149        let mut stamps = Vec::new();
5150        let mut datagram = vec![0u8; 2048];
5151        for _ in 0..3 {
5152            let (len, _) =
5153                tokio::time::timeout(Duration::from_secs(2), listener.recv_from(&mut datagram))
5154                    .await
5155                    .expect("no timeout")
5156                    .expect("receives");
5157            let packet =
5158                Packet::decode(&Bytes::copy_from_slice(&datagram[..len])).expect("a valid packet");
5159            stamps.push(packet.timestamp);
5160        }
5161
5162        assert_eq!(
5163            stamps[1].wrapping_sub(stamps[0]),
5164            80,
5165            "80 samples sent must advance the clock by 80, not by the configured 160"
5166        );
5167        assert_eq!(stamps[2].wrapping_sub(stamps[1]), 80);
5168    }
5169
5170    /// A dynamic payload number means only what SDP assigned it. A number that names neither
5171    /// the negotiated codec nor a known static codec is loss, and that loss must be observable.
5172    #[tokio::test]
5173    async fn an_unknown_payload_type_is_dropped_rather_than_decoded_as_audio() {
5174        let raw = UdpSocket::bind(any()).await.expect("binds");
5175        let mut config = Config::new(raw.local_addr().expect("address"), Codec::Pcmu);
5176        config.jitter_depth = 1;
5177        config.jitter_max_depth = None;
5178        config.dtmf_payload_type = None;
5179        let session = MediaSession::start(any(), config).await.expect("starts");
5180
5181        // First establish the SSRC, then offer an unassigned dynamic payload on that same stream.
5182        let valid = Packet::new(0, 1, 160, 7, Bytes::from(vec![0xFF; 160]));
5183        raw.send_to(&valid.encode(), session.local_addr())
5184            .await
5185            .expect("sends");
5186        let heard = session.record_at_least(160, DELIVERY_BOUND).await;
5187        assert_eq!(heard.len(), 160);
5188
5189        let unknown = Packet::new(96, 2, 320, 7, Bytes::from_static(&[1, 2, 3, 4]));
5190        raw.send_to(&unknown.encode(), session.local_addr())
5191            .await
5192            .expect("sends");
5193
5194        let deadline = tokio::time::Instant::now() + DELIVERY_BOUND;
5195        while session.discard_counts().unknown_payload_type == 0 {
5196            assert!(
5197                tokio::time::Instant::now() < deadline,
5198                "the unknown payload never reached the discard site"
5199            );
5200            tokio::time::sleep(Duration::from_millis(5)).await;
5201        }
5202
5203        let after = session.record_until_idle(Duration::from_millis(200)).await;
5204        assert!(
5205            after.is_empty(),
5206            "an unknown payload must not become audio samples: {after:?}"
5207        );
5208        assert_eq!(session.discard_counts().unknown_payload_type, 1);
5209    }
5210
5211    /// S-36 / RFC 3264 §6.1: each description assigns the dynamic number its author receives.
5212    /// Therefore a session sends with the peer's answer number while accepting the different
5213    /// number from its own offer; collapsing both directions into one number loses one stream.
5214    #[tokio::test]
5215    async fn asymmetric_dynamic_payload_types_are_honoured_in_both_directions() {
5216        let raw = UdpSocket::bind(any()).await.expect("binds");
5217        let mut config = Config::new(raw.local_addr().expect("address"), Codec::Pcmu);
5218        config.payload_type = Some(96);
5219        config.receive_payload_type = Some(111);
5220        config.jitter_depth = 1;
5221        config.jitter_max_depth = None;
5222        config.dtmf_payload_type = None;
5223        let session = MediaSession::start(any(), config).await.expect("starts");
5224
5225        session.send(vec![0; 160]).await;
5226        let mut datagram = vec![0; 2048];
5227        let (len, _) = tokio::time::timeout(DELIVERY_BOUND, raw.recv_from(&mut datagram))
5228            .await
5229            .expect("outbound packet arrives")
5230            .expect("receives");
5231        let outbound = Packet::decode(&Bytes::copy_from_slice(&datagram[..len])).expect("RTP");
5232        assert_eq!(outbound.payload_type, 96, "send with the peer's number");
5233
5234        let inbound = Packet::new(111, 1, 160, 7, Bytes::from(vec![0xFF; 160]));
5235        raw.send_to(&inbound.encode(), session.local_addr())
5236            .await
5237            .expect("sends inbound packet");
5238        let heard = session.record_at_least(160, DELIVERY_BOUND).await;
5239        assert_eq!(heard.len(), 160, "receive with our number");
5240    }
5241
5242    /// M-32's failing-first witness: unlike every other media discard in the original census,
5243    /// this loss had neither a trace nor a number. Fill the application queue, offer one more
5244    /// complete keypress, and assert the loss itself rather than a timeout in a consumer.
5245    #[tokio::test]
5246    async fn a_dtmf_digit_refused_by_the_application_queue_is_counted() {
5247        let (audio, _audio_rx) = mpsc::channel(1);
5248        let (encoded, _encoded_rx) = mpsc::channel(1);
5249        let relay = AtomicBool::new(false);
5250        let discards = Arc::new(DiscardMeters::default());
5251        let delivery = Delivery {
5252            audio: &audio,
5253            encoded: &encoded,
5254            relay: &relay,
5255            discards: &discards,
5256        };
5257        let (digits_tx, _digits_rx) = mpsc::channel(32);
5258        let arrivals = Arc::new(watch::Sender::new(0));
5259        let digits = Keypresses {
5260            to: digits_tx,
5261            arrivals,
5262        };
5263        let mut decoding = Decoding::for_codec(Codec::Pcmu, 1).expect("codec");
5264        let mut receiver = sipx_rtp::dtmf::Receiver::new();
5265        let config = Config::new(any(), Codec::Pcmu);
5266        let stop = Stop::default();
5267
5268        for sequence in 0u16..33 {
5269            let event = DtmfEvent {
5270                digit: Digit::Number(5),
5271                end: true,
5272                volume: 10,
5273                duration: 160,
5274            };
5275            let packet = Packet::new(
5276                dtmf::DEFAULT_PAYLOAD_TYPE,
5277                sequence,
5278                u32::from(sequence) * 160,
5279                1,
5280                event.encode(),
5281            );
5282            assert!(
5283                deliver(
5284                    &delivery,
5285                    &mut decoding,
5286                    &digits,
5287                    &mut receiver,
5288                    &config,
5289                    &stop,
5290                    &packet,
5291                )
5292                .await
5293            );
5294        }
5295
5296        assert_eq!(discards.snapshot().dtmf_delivery_failures, 1);
5297    }
5298
5299    /// Once a stream is established, a packet from a different synchronisation source is
5300    /// dropped. Without this, one forged packet with a high sequence number advances the
5301    /// jitter buffer past every genuine packet still to come, and the call goes silent.
5302    #[tokio::test]
5303    async fn a_packet_from_another_source_cannot_silence_the_stream() {
5304        let (left, right) = pair(Codec::Pcmu).await;
5305
5306        right.play(&tone(320), 160).await;
5307        assert_eq!(left.record_at_least(320, DELIVERY_BOUND).await.len(), 320);
5308
5309        // A forged packet: valid RTP, different SSRC, sequence number far in the future.
5310        let forged = Packet::new(0, 60_000, 0, 0xBAD0_BAD0, Bytes::from(vec![0xFFu8; 160]));
5311        let attacker = UdpSocket::bind(any()).await.expect("binds");
5312        attacker
5313            .send_to(&forged.encode(), left.local_addr())
5314            .await
5315            .expect("sends");
5316        // Order on the observable effect, not on elapsed wall time: under load a fixed sleep can
5317        // let the genuine stream resume before this packet reaches the discard site.
5318        let deadline = tokio::time::Instant::now() + DELIVERY_BOUND;
5319        while left.discard_counts().foreign_ssrc == 0 {
5320            assert!(
5321                tokio::time::Instant::now() < deadline,
5322                "the foreign packet never reached the discard site"
5323            );
5324            tokio::time::sleep(Duration::from_millis(5)).await;
5325        }
5326
5327        // The genuine stream still gets through.
5328        let more = tone(320);
5329        right.play(&more, 160).await;
5330        let heard = left.record_at_least(more.len(), DELIVERY_BOUND).await;
5331        assert_eq!(
5332            g711::ulaw_encode_all(&more),
5333            g711::ulaw_encode_all(&heard),
5334            "the forged packet must not have poisoned the buffer"
5335        );
5336    }
5337
5338    /// RFC 3550 §6.4.2: a report's first field is the SSRC of the *reporter*, and §8.1
5339    /// requires that to be the SSRC the reporter's own RTP carries; each report block names
5340    /// the source it describes. A report saying "SSRC 0 heard SSRC 0" is unusable.
5341    #[tokio::test]
5342    async fn rtcp_reports_name_both_parties_by_their_real_ssrcs() {
5343        let (peer_media, peer_control) = adjacent_ports().await;
5344        let mut config = Config::new(peer_media.local_addr().expect("addr"), Codec::Pcmu);
5345        config.rtcp_interval = Some(Duration::from_millis(200));
5346        let session = MediaSession::start(any(), config).await.expect("binds");
5347
5348        // The peer speaks first, so the session latches its address and its
5349        // synchronisation source.
5350        for sequence in 1u16..=5 {
5351            let packet = Packet::new(
5352                0,
5353                sequence,
5354                u32::from(sequence) * 160,
5355                0x5EED_CAFE,
5356                Bytes::from(vec![0xFFu8; 160]),
5357            );
5358            peer_media
5359                .send_to(&packet.encode(), session.local_addr())
5360                .await
5361                .expect("sends");
5362            tokio::time::sleep(Duration::from_millis(10)).await;
5363        }
5364
5365        // And the session speaks, so the SSRC its own RTP carries is observable.
5366        session.play(&tone(320), 160).await;
5367        let mut datagram = vec![0u8; 2048];
5368        let (len, _) =
5369            tokio::time::timeout(Duration::from_secs(2), peer_media.recv_from(&mut datagram))
5370                .await
5371                .expect("no timeout")
5372                .expect("receives");
5373        let rtp_ssrc = Packet::decode(&Bytes::copy_from_slice(&datagram[..len]))
5374            .expect("a valid packet")
5375            .ssrc;
5376
5377        let (len, _) = tokio::time::timeout(
5378            Duration::from_secs(3),
5379            peer_control.recv_from(&mut datagram),
5380        )
5381        .await
5382        .expect("a report arrives")
5383        .expect("receives");
5384        let packets =
5385            Rtcp::decode_compound(&Bytes::copy_from_slice(&datagram[..len])).expect("valid RTCP");
5386        let (reporter, blocks) = match &packets[0] {
5387            Rtcp::Sender(report) => (report.ssrc, report.reports.clone()),
5388            Rtcp::Receiver(report) => (report.ssrc, report.reports.clone()),
5389            other => panic!("a report must lead, got {other:?}"),
5390        };
5391        assert_eq!(
5392            reporter, rtp_ssrc,
5393            "the reporter names itself by the SSRC its RTP carries"
5394        );
5395        assert_eq!(blocks[0].ssrc, 0x5EED_CAFE, "the block names the far end");
5396    }
5397
5398    /// RFC 3550 §6.1: every RTCP packet travels in a compound of at least two, the first a
5399    /// report, and each compound carries an SDES CNAME. The CNAME is what lets a receiver
5400    /// tie streams to one participant across an SSRC change, so it must be stable.
5401    #[tokio::test]
5402    async fn rtcp_goes_out_compound_with_a_stable_cname() {
5403        let (peer_media, peer_control) = adjacent_ports().await;
5404        let mut config = Config::new(peer_media.local_addr().expect("addr"), Codec::Pcmu);
5405        config.rtcp_interval = Some(Duration::from_millis(150));
5406        let session = MediaSession::start(any(), config).await.expect("binds");
5407
5408        // The peer speaks so there is something to report on.
5409        for sequence in 1u16..=5 {
5410            let packet = Packet::new(
5411                0,
5412                sequence,
5413                u32::from(sequence) * 160,
5414                0xABCD,
5415                Bytes::from(vec![0xFFu8; 160]),
5416            );
5417            peer_media
5418                .send_to(&packet.encode(), session.local_addr())
5419                .await
5420                .expect("sends");
5421            tokio::time::sleep(Duration::from_millis(10)).await;
5422        }
5423
5424        let mut cnames = Vec::new();
5425        let mut datagram = vec![0u8; 2048];
5426        for _ in 0..2 {
5427            let (len, _) = tokio::time::timeout(
5428                Duration::from_secs(3),
5429                peer_control.recv_from(&mut datagram),
5430            )
5431            .await
5432            .expect("a report arrives")
5433            .expect("receives");
5434            let packets = Rtcp::decode_compound(&Bytes::copy_from_slice(&datagram[..len]))
5435                .expect("valid RTCP");
5436            assert!(packets.len() >= 2, "a lone report is not a compound");
5437            assert!(
5438                matches!(packets[0], Rtcp::Sender(_) | Rtcp::Receiver(_)),
5439                "a report leads the compound"
5440            );
5441            let sdes = packets
5442                .iter()
5443                .find_map(|packet| match packet {
5444                    Rtcp::Sdes(sdes) => Some(sdes),
5445                    _ => None,
5446                })
5447                .expect("an SDES in every compound");
5448            let cname = sdes.chunks[0]
5449                .items
5450                .iter()
5451                .find(|item| item.kind == sipx_rtp::rtcp::SDES_CNAME)
5452                .expect("a CNAME item");
5453            assert!(!cname.value.is_empty());
5454            cnames.push(cname.value.clone());
5455        }
5456        assert_eq!(cnames[0], cnames[1], "the CNAME does not change mid-call");
5457    }
5458
5459    /// RFC 4733 §2.5.1.2: every packet of one telephone event carries the *same* timestamp
5460    /// — the event's start — while being sent one packetisation interval apart, so its
5461    /// transit grows by one interval per packet by design. RFC 3550 §6.4.1 defines jitter
5462    /// over packets whose timestamps track sampling instants; a keypress must not fabricate
5463    /// jitter, but it must still count for loss and sequence continuity.
5464    #[tokio::test]
5465    async fn a_keypress_does_not_register_as_jitter() {
5466        let raw = UdpSocket::bind(any()).await.expect("binds");
5467        let session = MediaSession::start(
5468            any(),
5469            Config::new(raw.local_addr().expect("addr"), Codec::Pcmu),
5470        )
5471        .await
5472        .expect("binds");
5473
5474        // One long keypress: same timestamp throughout, spaced a packet interval apart,
5475        // with one packet lost in flight.
5476        for sequence in 1u16..=10 {
5477            if sequence == 4 {
5478                continue;
5479            }
5480            let duration = sequence * 160;
5481            let event = DtmfEvent::new(Digit::Number(5), duration);
5482            let packet = Packet::new(101, sequence, 5000, 0xAB, event.encode());
5483            raw.send_to(&packet.encode(), session.local_addr())
5484                .await
5485                .expect("sends");
5486            tokio::time::sleep(Duration::from_millis(20)).await;
5487        }
5488        // As in `a_session_reports_the_loss_it_saw`: wait for the nine to arrive rather than
5489        // sleeping 100 ms and assuming they have (`X-29`). `extended_highest_sequence` and
5490        // `cumulative_lost` are asserted exactly, so a straggler reports loss nobody injected.
5491        // Same precondition as that test, and it is the same fragility: the counter only implies
5492        // the statistics because nothing suspends between `received.fetch_add` (`:2258`) and
5493        // `note_arrival`'s lock (`:2301-2313`) on a `current_thread` runtime.
5494        let deadline = tokio::time::Instant::now() + Duration::from_secs(10);
5495        while session.packets_received() != 9 {
5496            assert!(
5497                tokio::time::Instant::now() < deadline,
5498                "the nine hand-sent keypress packets never reached the receive path"
5499            );
5500            tokio::time::sleep(Duration::from_millis(5)).await;
5501        }
5502
5503        let block = session.stats().await;
5504        assert_eq!(block.jitter, 0, "a keypress is not network jitter");
5505        assert_eq!(
5506            block.extended_highest_sequence, 10,
5507            "the events still advance the sequence accounting"
5508        );
5509        assert_eq!(block.cumulative_lost, 1, "and still count for loss");
5510    }
5511
5512    /// RFC 3550 §6.4: a participant that sent data during the interval sends a *sender*
5513    /// report. The RR it would otherwise send carries no NTP/RTP pair and no counts, and
5514    /// without those the far end can never compute round-trip time or line the clocks up.
5515    #[tokio::test]
5516    async fn an_active_sender_reports_with_a_sender_report() {
5517        let (peer_media, peer_control) = adjacent_ports().await;
5518        let mut config = Config::new(peer_media.local_addr().expect("addr"), Codec::Pcmu);
5519        config.rtcp_interval = Some(Duration::from_millis(200));
5520        let session = MediaSession::start(any(), config).await.expect("binds");
5521
5522        // Both directions are busy: the peer speaks once, the session streams audio.
5523        let packet = Packet::new(0, 1, 160, 0xABCD, Bytes::from(vec![0xFFu8; 160]));
5524        peer_media
5525            .send_to(&packet.encode(), session.local_addr())
5526            .await
5527            .expect("sends");
5528        session.play(&tone(1600), 160).await;
5529
5530        // The first interval may legitimately elapse before the first packet leaves — an
5531        // RR is correct then — so wait for the first report from an interval in which
5532        // data went out. A stack that never sends one fails here by timing out.
5533        let mut datagram = vec![0u8; 2048];
5534        let report = tokio::time::timeout(Duration::from_secs(5), async {
5535            loop {
5536                let (len, _) = peer_control
5537                    .recv_from(&mut datagram)
5538                    .await
5539                    .expect("receives");
5540                let packets = Rtcp::decode_compound(&Bytes::copy_from_slice(&datagram[..len]))
5541                    .expect("valid RTCP");
5542                match &packets[0] {
5543                    Rtcp::Sender(report) => return report.clone(),
5544                    Rtcp::Receiver(_) => {}
5545                    other => panic!("a report must lead the compound, got {other:?}"),
5546                }
5547            }
5548        })
5549        .await
5550        .expect("an active sender must send a sender report");
5551
5552        assert!(report.packet_count >= 1, "it counts the packets it sent");
5553        assert!(report.octet_count >= 160, "and the payload bytes");
5554        // RFC 3550 §4: the high word is seconds since 1900 — around 3.97 billion now.
5555        let seconds = report.ntp_timestamp >> 32;
5556        assert!(
5557            (3_700_000_000..4_294_967_295).contains(&seconds),
5558            "the NTP word counts seconds since 1900: {seconds}"
5559        );
5560        assert_eq!(report.reports.len(), 1, "reception is appended as a block");
5561    }
5562
5563    fn peer_sender_report(ntp_timestamp: u64) -> Bytes {
5564        Rtcp::encode_compound(&[
5565            Rtcp::Sender(sipx_rtp::rtcp::SenderReport {
5566                ssrc: 0x5EED_CAFE,
5567                ntp_timestamp,
5568                rtp_timestamp: 160,
5569                packet_count: 1,
5570                octet_count: 160,
5571                reports: Vec::new(),
5572            }),
5573            Rtcp::Sdes(Sdes::cname(0x5EED_CAFE, "peer@example.invalid")),
5574        ])
5575    }
5576
5577    async fn wait_for_rtcp_echo(socket: &UdpSocket, expected: u32) -> SocketAddr {
5578        let mut datagram = vec![0u8; 2048];
5579        tokio::time::timeout(Duration::from_secs(3), async {
5580            loop {
5581                let (len, from) = socket.recv_from(&mut datagram).await.expect("receives");
5582                let packets = Rtcp::decode_compound(&Bytes::copy_from_slice(&datagram[..len]))
5583                    .expect("valid RTCP");
5584                let echoed = packets.iter().any(|packet| match packet {
5585                    Rtcp::Sender(report) => report
5586                        .reports
5587                        .iter()
5588                        .any(|block| block.last_sender_report == expected),
5589                    Rtcp::Receiver(report) => report
5590                        .reports
5591                        .iter()
5592                        .any(|block| block.last_sender_report == expected),
5593                    Rtcp::Sdes(_) | Rtcp::Other { .. } => false,
5594                });
5595                if echoed {
5596                    return from;
5597                }
5598            }
5599        })
5600        .await
5601        .expect("the peer sender report is processed and echoed")
5602    }
5603
5604    /// `MUX-PKT-1`, failing first: RTCP arriving on the negotiated RTP port reaches RTCP state,
5605    /// and the response leaves from and returns to that same port.
5606    #[tokio::test]
5607    async fn muxed_rtcp_arriving_on_the_rtp_port_is_processed_not_dropped() {
5608        let peer = UdpSocket::bind(any()).await.expect("binds peer");
5609        let mut config = Config::new(peer.local_addr().expect("peer address"), Codec::Pcmu);
5610        config.rtcp_mode = sipx_sdp::RtcpMode::Mux;
5611        config.rtcp_interval = Some(Duration::from_millis(20));
5612        let session = MediaSession::start(any(), config).await.expect("starts");
5613
5614        let rtp = Packet::new(0, 1, 160, 0x5EED_CAFE, Bytes::from(vec![0xFF; 160]));
5615        peer.send_to(&rtp.encode(), session.local_addr())
5616            .await
5617            .expect("sends RTP");
5618        let ntp = 0x0123_4567_89AB_CDEF;
5619        peer.send_to(&peer_sender_report(ntp), session.local_addr())
5620            .await
5621            .expect("sends RTCP on the RTP port");
5622
5623        let report_source = wait_for_rtcp_echo(&peer, sipx_rtp::quality::middle_32(ntp)).await;
5624        assert_eq!(
5625            report_source,
5626            session.local_addr(),
5627            "muxed RTCP leaves from the exact socket address advertised for RTP"
5628        );
5629        session.stop();
5630    }
5631
5632    /// `MUX-PKT-2`: omission of mux leaves the established adjacent control-port path live.
5633    #[tokio::test]
5634    async fn separate_rtcp_still_uses_and_processes_the_control_port() {
5635        let (peer_media, peer_control) = adjacent_ports().await;
5636        let mut config = Config::new(peer_media.local_addr().expect("peer address"), Codec::Pcmu);
5637        config.rtcp_mode = sipx_sdp::RtcpMode::Separate;
5638        config.rtcp_interval = Some(Duration::from_millis(20));
5639        let session = MediaSession::start(any(), config).await.expect("starts");
5640
5641        let rtp = Packet::new(0, 1, 160, 0x5EED_CAFE, Bytes::from(vec![0xFF; 160]));
5642        peer_media
5643            .send_to(&rtp.encode(), session.local_addr())
5644            .await
5645            .expect("sends RTP");
5646        let control_addr = session
5647            .rtcp_socket
5648            .as_ref()
5649            .expect("the session bound its adjacent control port")
5650            .local_addr()
5651            .expect("control address");
5652        let ntp = 0x89AB_CDEF_0123_4567;
5653        peer_control
5654            .send_to(&peer_sender_report(ntp), control_addr)
5655            .await
5656            .expect("sends RTCP on the control port");
5657
5658        let _ = wait_for_rtcp_echo(&peer_control, sipx_rtp::quality::middle_32(ntp)).await;
5659        session.stop();
5660    }
5661
5662    /// RFC 3550 §6.3.1: each report interval is drawn uniformly from [0.5, 1.5] of the
5663    /// computed value and divided by e − 3/2 ≈ 1.21828. Without the randomness every
5664    /// participant that computed the same interval reports at the same instant, forever.
5665    #[test]
5666    fn the_rtcp_interval_is_randomised_over_the_rfc_range() {
5667        let base = Duration::from_secs(5);
5668        let compensation = std::f64::consts::E - 1.5;
5669
5670        let low = randomized_rtcp_interval(base, 0.0);
5671        let high = randomized_rtcp_interval(base, 1.0);
5672        assert!((low.as_secs_f64() - 2.5 / compensation).abs() < 1e-9);
5673        assert!((high.as_secs_f64() - 7.5 / compensation).abs() < 1e-9);
5674        assert!(
5675            low < base && base < high,
5676            "the range straddles the configured value: {low:?}..{high:?}"
5677        );
5678
5679        // A draw outside the unit range must not panic the media path or leave the range.
5680        assert!(randomized_rtcp_interval(base, f64::NAN) >= low);
5681        assert!(randomized_rtcp_interval(base, 7.0) <= high);
5682    }
5683
5684    #[test]
5685    fn codecs_map_to_their_static_payload_types() {
5686        assert_eq!(Codec::Pcmu.payload_type(), 0);
5687        assert_eq!(Codec::Pcma.payload_type(), 8);
5688        assert_eq!(Codec::L16.payload_type(), 11);
5689        assert_eq!(Codec::from_payload_type(0), Some(Codec::Pcmu));
5690        assert_eq!(Codec::from_payload_type(8), Some(Codec::Pcma));
5691        assert_eq!(Codec::from_payload_type(11), Some(Codec::L16));
5692        assert_eq!(Codec::from_payload_type(10), None, "stereo L16 is not ours");
5693        assert_eq!(Codec::from_payload_type(9), None, "G.722 is not ours");
5694    }
5695}