Skip to main content

rvoip_rtp_core/session/
mod.rs

1//! RTP Session Management
2//!
3//! This module provides functionality for managing RTP sessions, including
4//! configuration, packet sending/receiving, and jitter buffer management.
5
6mod scheduling;
7mod stream;
8
9pub use scheduling::{RtpScheduler, RtpSchedulerStats};
10pub use stream::{RtpStream, RtpStreamStats};
11
12use bytes::{Bytes, BytesMut};
13use dashmap::DashMap;
14use rand::Rng;
15use std::net::SocketAddr;
16use std::sync::atomic::{AtomicU16, Ordering};
17use std::sync::Arc;
18use std::time::Duration;
19use tokio::net::UdpSocket;
20use tokio::sync::{broadcast, mpsc};
21use tokio::task::JoinHandle;
22use tracing::{debug, error, info, trace, warn};
23
24use crate::error::Error;
25use crate::packet::{RtpHeader, RtpPacket};
26use crate::transport::{
27    RtpTransport, RtpTransportBufferConfig, RtpTransportConfig, UdpRtpTransport,
28};
29use crate::{Result, RtpSsrc, RtpTimestamp};
30
31#[cfg(feature = "memory-diagnostics")]
32fn spawn_memory_tracked<F>(kind: &'static str, future: F) -> JoinHandle<F::Output>
33where
34    F: std::future::Future + Send + 'static,
35    F::Output: Send + 'static,
36{
37    rvoip_infra_common::memory_diagnostics::spawn_tracked(kind, future)
38}
39
40#[cfg(not(feature = "memory-diagnostics"))]
41fn spawn_memory_tracked<F>(_: &'static str, future: F) -> JoinHandle<F::Output>
42where
43    F: std::future::Future + Send + 'static,
44    F::Output: Send + 'static,
45{
46    tokio::spawn(future)
47}
48
49/// Bounded queue depth for per-session RTP send/event channels.
50///
51/// RTP is real-time traffic; keeping many seconds of packet backlog per call
52/// hides overload and retains packet payloads. At 20 ms packets, 64 entries is
53/// roughly 1.3 seconds of headroom for one stream.
54pub const RTP_SESSION_CHANNEL_CAPACITY: usize = 64;
55
56/// Small best-effort queue for the legacy polling receive API.
57///
58/// Media-core consumes RTP packets through the event broadcast path, so this
59/// queue must not become an unbounded duplicate packet buffer when nobody calls
60/// [`RtpSession::receive_packet`].
61pub const RTP_SESSION_RECEIVE_QUEUE_CAPACITY: usize = 32;
62
63/// RTP session queue sizing.
64#[derive(Debug, Clone, Copy, PartialEq, Eq)]
65pub struct RtpSessionBufferConfig {
66    /// Bounded sender queue capacity in RTP packets.
67    pub sender_channel_capacity: usize,
68    /// Bounded legacy polling receive queue capacity in RTP packets.
69    pub receiver_channel_capacity: usize,
70    /// Broadcast ring capacity for RTP session events.
71    pub event_channel_capacity: usize,
72}
73
74impl Default for RtpSessionBufferConfig {
75    fn default() -> Self {
76        Self {
77            sender_channel_capacity: RTP_SESSION_CHANNEL_CAPACITY,
78            receiver_channel_capacity: RTP_SESSION_RECEIVE_QUEUE_CAPACITY,
79            event_channel_capacity: RTP_SESSION_CHANNEL_CAPACITY,
80        }
81    }
82}
83
84/// Stats for an RTP session
85#[derive(Debug, Clone, Default)]
86pub struct RtpSessionStats {
87    /// Total packets sent
88    pub packets_sent: u64,
89
90    /// Total packets received
91    pub packets_received: u64,
92
93    /// Total bytes sent
94    pub bytes_sent: u64,
95
96    /// Total bytes received
97    pub bytes_received: u64,
98
99    /// Packets lost (based on sequence numbers)
100    pub packets_lost: u64,
101
102    /// Duplicate packets received
103    pub packets_duplicated: u64,
104
105    /// Out-of-order packets received
106    pub packets_out_of_order: u64,
107
108    /// Packets discarded by jitter buffer (too old)
109    pub packets_discarded_by_jitter: u64,
110
111    /// Current jitter estimate (in milliseconds)
112    pub jitter_ms: f64,
113
114    /// Remote address of the most recent packet
115    pub remote_addr: Option<SocketAddr>,
116}
117
118/// Snapshot of bounded queue occupancy inside an RTP session.
119#[derive(Debug, Clone, Copy, Default)]
120pub struct RtpSessionQueueDiagnostics {
121    /// Packets waiting to be sent by the RTP send task.
122    pub sender_queue_packets: usize,
123    /// Configured sender queue capacity.
124    pub sender_capacity_packets: usize,
125    /// Packets waiting in the receive queue for explicit `receive_packet` users.
126    pub receiver_queue_packets: usize,
127    /// Configured receiver queue capacity.
128    pub receiver_capacity_packets: usize,
129    /// Events retained in the broadcast ring.
130    pub event_queue_events: usize,
131    /// Current subscribers to the event broadcast ring.
132    pub event_receiver_count: usize,
133    #[cfg(feature = "memory-diagnostics")]
134    /// Current SSRC stream entries retained by this session.
135    pub stream_count: usize,
136}
137
138/// RTP session configuration options
139#[derive(Debug, Clone)]
140pub struct RtpSessionConfig {
141    /// Local address to bind to
142    pub local_addr: SocketAddr,
143
144    /// Remote address to send packets to
145    pub remote_addr: Option<SocketAddr>,
146
147    /// SSRC to use for sending packets
148    pub ssrc: Option<RtpSsrc>,
149
150    /// Payload type
151    pub payload_type: u8,
152
153    /// Clock rate for the payload type (needed for jitter buffer)
154    pub clock_rate: u32,
155
156    /// Jitter buffer size in packets
157    pub jitter_buffer_size: Option<usize>,
158
159    /// Maximum packet age in the jitter buffer (ms)
160    pub max_packet_age_ms: Option<u32>,
161
162    /// Enable jitter buffer
163    pub enable_jitter_buffer: bool,
164
165    /// RTP session queue and reusable send-buffer sizing.
166    pub session_buffer_config: RtpSessionBufferConfig,
167
168    /// UDP transport buffer sizing used when the session creates its transport.
169    pub transport_buffer_config: RtpTransportBufferConfig,
170}
171
172impl Default for RtpSessionConfig {
173    fn default() -> Self {
174        Self {
175            local_addr: "0.0.0.0:0".parse().unwrap(),
176            remote_addr: None,
177            ssrc: None,
178            payload_type: 0,
179            clock_rate: 8000, // Default for most audio codecs (8kHz)
180            jitter_buffer_size: Some(50),
181            max_packet_age_ms: Some(200),
182            enable_jitter_buffer: true,
183            session_buffer_config: RtpSessionBufferConfig::default(),
184            transport_buffer_config: RtpTransportBufferConfig::default(),
185        }
186    }
187}
188
189/// Lock-free handle for sending RTP packets through an existing
190/// [`RtpSession`] without touching the outer `Arc<Mutex<RtpSession>>`.
191///
192/// Cheap to clone (3 Arcs + 2 small scalars). Issued by
193/// [`RtpSession::send_handle`]; multiple handles for the same session
194/// stay in sync because they share the same sequence atomic.
195#[derive(Clone)]
196pub struct RtpSendHandle {
197    sender: mpsc::Sender<RtpPacket>,
198    ssrc: RtpSsrc,
199    sequence: Arc<AtomicU16>,
200    default_payload_type: u8,
201}
202
203impl RtpSendHandle {
204    /// Send an RTP packet with the session's default payload type.
205    pub async fn send_packet(
206        &self,
207        timestamp: RtpTimestamp,
208        payload: Bytes,
209        marker: bool,
210    ) -> Result<()> {
211        self.send_packet_with_pt(timestamp, payload, marker, self.default_payload_type)
212            .await
213    }
214
215    /// Send an RTP packet overriding the configured payload type
216    /// (e.g. RFC 4733 telephone-event PT 101).
217    pub async fn send_packet_with_pt(
218        &self,
219        timestamp: RtpTimestamp,
220        payload: Bytes,
221        marker: bool,
222        payload_type: u8,
223    ) -> Result<()> {
224        let sequence = self.sequence.fetch_add(1, Ordering::Relaxed);
225        let mut header = RtpHeader::new(payload_type, sequence, timestamp, self.ssrc);
226        header.marker = marker;
227        let packet = RtpPacket::new(header, payload);
228        self.sender
229            .send(packet)
230            .await
231            .map_err(|_| Error::SessionError("Failed to send packet".to_string()))
232    }
233
234    /// Get the session's SSRC (immutable post-construction).
235    pub fn ssrc(&self) -> RtpSsrc {
236        self.ssrc
237    }
238}
239
240/// Events emitted by the RTP session
241#[derive(Debug, Clone)]
242pub enum RtpSessionEvent {
243    /// New packet received
244    PacketReceived(RtpPacket),
245
246    /// Error in the session
247    Error(Error),
248
249    /// BYE RTCP packet received (a party is leaving the session)
250    Bye {
251        /// SSRC of the source that sent the BYE
252        ssrc: RtpSsrc,
253
254        /// Optional reason text
255        reason: Option<String>,
256    },
257
258    /// New stream detected with a specific SSRC
259    /// This event is emitted as soon as the first packet for a new SSRC is received,
260    /// even if the packet is being held in a jitter buffer.
261    NewStreamDetected {
262        /// SSRC of the new stream
263        ssrc: RtpSsrc,
264    },
265
266    /// RTCP Sender Report received
267    RtcpSenderReport {
268        /// SSRC of the sender
269        ssrc: RtpSsrc,
270
271        /// NTP timestamp
272        ntp_timestamp: crate::packet::rtcp::NtpTimestamp,
273
274        /// RTP timestamp
275        rtp_timestamp: RtpTimestamp,
276
277        /// Packet count
278        packet_count: u32,
279
280        /// Octet count
281        octet_count: u32,
282
283        /// Report blocks
284        report_blocks: Vec<crate::packet::rtcp::RtcpReportBlock>,
285    },
286
287    /// RTCP Receiver Report received
288    RtcpReceiverReport {
289        /// SSRC of the receiver
290        ssrc: RtpSsrc,
291
292        /// Report blocks
293        report_blocks: Vec<crate::packet::rtcp::RtcpReportBlock>,
294    },
295
296    /// RFC 4733 telephone-event (DTMF / fax / modem tone) received.
297    /// Forwarded verbatim from the transport-level `RtpEvent::DtmfEvent`.
298    /// Consumers should forward the digit up to the application only on
299    /// the frame where `end_of_event == true` — RFC 4733 §2.5.1.3
300    /// requires three final retransmissions so the last three frames
301    /// of each tone all set the `E` bit — and dedup on `(ssrc, timestamp)`
302    /// which uniquely identifies a tone.
303    DtmfReceived {
304        /// Event code (0-15 for DTMF).
305        event: u8,
306        /// End-of-event `E` bit.
307        end_of_event: bool,
308        /// -dBm0 volume (0-63).
309        volume: u8,
310        /// Duration in RTP timestamp units.
311        duration: u16,
312        /// RTP packet timestamp (dedup key for retransmits).
313        timestamp: u32,
314        /// SSRC that sent the event.
315        ssrc: RtpSsrc,
316    },
317}
318
319/// RTP session for sending and receiving RTP packets
320///
321/// This class manages an RTP session, including sending and receiving packets,
322/// jitter buffer management, and demultiplexing of multiple streams.
323///
324/// # SSRC Demultiplexing
325///
326/// An RTP session can receive packets from multiple sources, each identified by
327/// a unique Synchronization Source identifier (SSRC). This implementation
328/// automatically demultiplexes incoming packets based on their SSRC:
329///
330/// 1. When a packet arrives, its SSRC is extracted
331/// 2. If this is the first packet from this SSRC, a new stream is created
332/// 3. The packet is processed by the appropriate stream, which handles:
333///    - Sequence number tracking
334///    - Jitter calculation
335///    - Duplicate detection
336///    - Packet reordering (via jitter buffer)
337///
338/// Each stream maintains its own statistics and state. You can access information
339/// about individual streams using the `get_stream()`, `get_all_streams()`, and
340/// `stream_count()` methods.
341///
342/// This approach aligns with RFC 3550 Section 8.2, which describes how to handle
343/// multiple sources in a single RTP session.
344pub struct RtpSession {
345    /// Session configuration
346    config: RtpSessionConfig,
347
348    /// SSRC for this session
349    ssrc: RtpSsrc,
350
351    /// Transport for sending/receiving packets
352    transport: Arc<dyn RtpTransport>,
353
354    /// Map of received streams by SSRC. `DashMap` so the per-packet
355    /// demultiplex hot path (`session/mod.rs:620`+) doesn't serialise
356    /// every receive through a single mutex, and so `get_stream` /
357    /// `stream_count` readers don't block the demux task.
358    streams: Arc<DashMap<RtpSsrc, RtpStream>>,
359
360    /// Packet scheduler for sending packets
361    scheduler: Option<RtpScheduler>,
362
363    /// Channel for receiving packets
364    receiver: mpsc::Receiver<RtpPacket>,
365
366    /// Channel for sending packets
367    sender: mpsc::Sender<RtpPacket>,
368
369    /// Whether received RTP packets should also be mirrored into the legacy
370    /// polling receive queue.
371    receive_queue_enabled: bool,
372
373    /// Event broadcaster
374    event_tx: broadcast::Sender<RtpSessionEvent>,
375
376    /// Receiving task handle
377    recv_task: Option<JoinHandle<()>>,
378
379    /// Sending task handle
380    send_task: Option<JoinHandle<()>>,
381
382    /// Session statistics. `parking_lot::Mutex` because every guard is
383    /// CPU-only (counter updates, snapshot reads); the std variant
384    /// added avoidable lock-acquire overhead on the send/recv hot
385    /// paths and forced everything to unwrap poison.
386    stats: Arc<parking_lot::Mutex<RtpSessionStats>>,
387
388    /// Media synchronization context
389    media_sync: Option<Arc<std::sync::RwLock<crate::sync::MediaSync>>>,
390
391    /// Whether the session is active
392    active: bool,
393
394    /// RTCP report generator
395    rtcp_generator: Option<crate::stats::reports::RtcpReportGenerator>,
396
397    /// RTCP sender task
398    rtcp_task: Option<JoinHandle<()>>,
399
400    /// Session bandwidth (bits per second)
401    bandwidth_bps: u32,
402
403    #[cfg(feature = "memory-diagnostics")]
404    _memory_guard: rvoip_infra_common::memory_diagnostics::ObjectGuard,
405    #[cfg(feature = "memory-diagnostics")]
406    _sender_channel_guard: rvoip_infra_common::memory_diagnostics::ObjectGuard,
407    #[cfg(feature = "memory-diagnostics")]
408    _receiver_channel_guard: rvoip_infra_common::memory_diagnostics::ObjectGuard,
409    #[cfg(feature = "memory-diagnostics")]
410    _event_channel_guard: rvoip_infra_common::memory_diagnostics::ObjectGuard,
411}
412
413impl RtpSession {
414    /// Create a new RTP session
415    pub async fn new(config: RtpSessionConfig) -> Result<Self> {
416        Self::new_with_receive_queue(config, true).await
417    }
418
419    /// Create a new RTP session for event-driven consumers.
420    ///
421    /// Packets are still emitted through [`RtpSessionEvent::PacketReceived`],
422    /// but they are not duplicated into the polling queue used by
423    /// [`RtpSession::receive_packet`].
424    pub async fn new_event_driven(config: RtpSessionConfig) -> Result<Self> {
425        Self::new_with_receive_queue(config, false).await
426    }
427
428    async fn new_with_receive_queue(
429        config: RtpSessionConfig,
430        receive_queue_enabled: bool,
431    ) -> Result<Self> {
432        let session_buffer_config = config.session_buffer_config;
433        let transport_buffer_config = config.transport_buffer_config;
434
435        // Generate SSRC if not provided
436        let ssrc = config.ssrc.unwrap_or_else(|| {
437            let mut rng = rand::thread_rng();
438            rng.gen::<u32>()
439        });
440
441        // Create transport config - respect provided ports!
442        let transport_config = RtpTransportConfig {
443            local_rtp_addr: config.local_addr,
444            local_rtcp_addr: None, // RTCP on same port for now
445            symmetric_rtp: true,
446            rtcp_mux: true, // Enable RTCP multiplexing by default
447            session_id: Some(format!("rtp-session-{}", ssrc)),
448            // Don't allocate a new port - use the one provided in config
449            use_port_allocator: false,
450            buffer_config: transport_buffer_config,
451        };
452
453        // Create UDP transport
454        let transport = Arc::new(UdpRtpTransport::new(transport_config).await?);
455
456        // Create channels for internal communication.
457        let (sender_tx, sender_rx) =
458            mpsc::channel(session_buffer_config.sender_channel_capacity.max(1));
459        let (receiver_tx, receiver_rx) =
460            mpsc::channel(session_buffer_config.receiver_channel_capacity.max(1));
461        let (event_tx, _) = broadcast::channel(session_buffer_config.event_channel_capacity.max(1));
462
463        // Create scheduler if needed
464        let scheduler = Some(RtpScheduler::new(
465            config.clock_rate,
466            rand::thread_rng().gen::<u16>(), // Random starting sequence
467            rand::thread_rng().gen::<u32>(), // Random starting timestamp
468        ));
469
470        // Create RTCP report generator
471        let hostname = hostname::get().unwrap_or_else(|_| "unknown".into());
472        let hostname_str = hostname.to_string_lossy();
473        let cname = format!(
474            "{}@{}",
475            std::env::var("USER").unwrap_or_else(|_| "user".to_string()),
476            hostname_str
477        );
478        let rtcp_generator = crate::stats::reports::RtcpReportGenerator::new(ssrc, cname);
479
480        let mut session = Self {
481            config,
482            ssrc,
483            transport,
484            streams: Arc::new(DashMap::new()),
485            scheduler,
486            receiver: receiver_rx,
487            sender: sender_tx,
488            receive_queue_enabled,
489            event_tx,
490            recv_task: None,
491            send_task: None,
492            stats: Arc::new(parking_lot::Mutex::new(RtpSessionStats::default())),
493            media_sync: None,
494            active: false,
495            rtcp_generator: Some(rtcp_generator),
496            rtcp_task: None,
497            bandwidth_bps: 64000, // Default bandwidth: 64 kbps
498            #[cfg(feature = "memory-diagnostics")]
499            _memory_guard: rvoip_infra_common::memory_diagnostics::ObjectGuard::new(
500                "rtp_core.rtp_session",
501                std::mem::size_of::<Self>(),
502            ),
503            #[cfg(feature = "memory-diagnostics")]
504            _sender_channel_guard: rvoip_infra_common::memory_diagnostics::ObjectGuard::new(
505                "rtp_core.rtp_session.sender_channel_capacity",
506                session_buffer_config.sender_channel_capacity * std::mem::size_of::<RtpPacket>(),
507            ),
508            #[cfg(feature = "memory-diagnostics")]
509            _receiver_channel_guard: rvoip_infra_common::memory_diagnostics::ObjectGuard::new(
510                "rtp_core.rtp_session.receiver_channel_capacity",
511                session_buffer_config.receiver_channel_capacity * std::mem::size_of::<RtpPacket>(),
512            ),
513            #[cfg(feature = "memory-diagnostics")]
514            _event_channel_guard: rvoip_infra_common::memory_diagnostics::ObjectGuard::new(
515                "rtp_core.rtp_session.event_broadcast_capacity",
516                session_buffer_config.event_channel_capacity
517                    * std::mem::size_of::<RtpSessionEvent>(),
518            ),
519        };
520
521        // Start the session
522        session.start(sender_rx, receiver_tx).await?;
523
524        Ok(session)
525    }
526
527    /// Start the session tasks
528    async fn start(
529        &mut self,
530        mut sender_rx: mpsc::Receiver<RtpPacket>,
531        receiver_tx: mpsc::Sender<RtpPacket>,
532    ) -> Result<()> {
533        if self.active {
534            return Ok(());
535        }
536
537        let transport = self.transport.clone();
538        let stats_send = self.stats.clone();
539        let stats_recv = self.stats.clone();
540        let remote_addr = self.config.remote_addr;
541        let event_tx_send = self.event_tx.clone();
542        let event_tx_recv = self.event_tx.clone();
543        let clock_rate = self.config.clock_rate;
544        let _payload_type = self.config.payload_type;
545        let ssrc = self.ssrc;
546        let streams_map = self.streams.clone();
547        let _jitter_buffer_enabled = self.config.enable_jitter_buffer;
548        let _jitter_size = self.config.jitter_buffer_size.unwrap_or(50);
549        let _max_age_ms = self.config.max_packet_age_ms.unwrap_or(200);
550        let receive_queue_enabled = self.receive_queue_enabled;
551
552        let media_sync = self.media_sync.clone();
553
554        // If we have a remote address, set it on the transport
555        if let Some(addr) = remote_addr {
556            // Set the remote RTP address on the UDP transport
557            if let Some(t) = transport.as_any().downcast_ref::<UdpRtpTransport>() {
558                t.set_remote_rtp_addr(addr).await;
559            }
560        }
561
562        // Prepare the scheduler's sequence state, but do not start its
563        // millisecond polling task. The current send paths route directly to
564        // `sender_tx` and only need the shared sequence atomic; no production
565        // code uses the scheduler queue. Starting one 1 ms timer per call was
566        // measurable CPU load under SIPp fan-out.
567        if let Some(scheduler) = &mut self.scheduler {
568            let sender_tx = self.sender.clone();
569            scheduler.set_sender(sender_tx);
570
571            // Set appropriate timestamp increment based on packet interval
572            let interval_ms = 20; // Default 20ms packet interval
573            let samples_per_packet = (clock_rate as f64 * (interval_ms as f64 / 1000.0)) as u32;
574            scheduler.set_interval(interval_ms, samples_per_packet);
575        }
576
577        // Start sending task
578        let send_transport = transport.clone();
579        let send_task = spawn_memory_tracked("rtp_core.rtp_session.send_task", async move {
580            let mut last_remote_addr = remote_addr;
581            let mut rtp_send_buffer = BytesMut::with_capacity(crate::DEFAULT_MAX_PACKET_SIZE);
582
583            while let Some(packet) = sender_rx.recv().await {
584                // Always try to get the current remote address from transport first
585                let dest =
586                    if let Some(t) = send_transport.as_any().downcast_ref::<UdpRtpTransport>() {
587                        // Check transport for current remote address
588                        match t.remote_rtp_addr().await {
589                            Some(addr) => {
590                                // Update our cached value
591                                last_remote_addr = Some(addr);
592                                addr
593                            }
594                            None => {
595                                // Fall back to cached value if transport doesn't have one
596                                if let Some(addr) = last_remote_addr {
597                                    addr
598                                } else {
599                                    // No destination address, can't send
600                                    warn!("No destination address for RTP packet, dropping");
601                                    continue;
602                                }
603                            }
604                        }
605                    } else {
606                        // Not a UDP transport, use cached value
607                        if let Some(addr) = last_remote_addr {
608                            addr
609                        } else {
610                            // No destination address, can't send
611                            warn!("No destination address for RTP packet, dropping");
612                            continue;
613                        }
614                    };
615
616                // Send the packet
617                debug!(
618                    "Sending RTP packet to {} (seq={}, timestamp={})",
619                    dest, packet.header.sequence_number, packet.header.timestamp
620                );
621
622                let send_result =
623                    if let Some(t) = send_transport.as_any().downcast_ref::<UdpRtpTransport>() {
624                        t.send_rtp_with_buffer(&packet, dest, &mut rtp_send_buffer)
625                            .await
626                    } else {
627                        send_transport.send_rtp(&packet, dest).await
628                    };
629
630                if let Err(e) = send_result {
631                    error!("Failed to send RTP packet: {}", e);
632
633                    // Broadcast error event
634                    let _ = event_tx_send.send(RtpSessionEvent::Error(e));
635                    continue;
636                }
637
638                debug!("Successfully sent RTP packet to {}", dest);
639
640                // Update stats
641                {
642                    let mut session_stats = stats_send.lock();
643                    session_stats.packets_sent += 1;
644                    session_stats.bytes_sent += packet.size() as u64;
645                }
646            }
647        });
648
649        // Start receiving task
650        let recv_transport = transport.clone();
651
652        // Subscribe to transport events to handle RTCP packets
653        let mut transport_events = recv_transport.subscribe();
654
655        let recv_task = spawn_memory_tracked("rtp_core.rtp_session.recv_task", async move {
656            // IMPORTANT: Only handle events from transport, no direct packet reception
657            // to avoid race conditions where two tasks read from the same socket
658            loop {
659                match transport_events.recv().await {
660                    Ok(crate::traits::RtpEvent::RtcpReceived { data, source: _ }) => {
661                        // Try to parse the RTCP packet
662                        if let Ok(rtcp_packet) = crate::packet::rtcp::RtcpPacket::parse(&data) {
663                            // Handle the RTCP packet based on its type
664                            match rtcp_packet {
665                                crate::packet::rtcp::RtcpPacket::Goodbye(bye) => {
666                                    // Extract the SSRC and reason
667                                    if !bye.sources.is_empty() {
668                                        let source_ssrc = bye.sources[0];
669
670                                        // Broadcast BYE event
671                                        let _ = event_tx_recv.send(RtpSessionEvent::Bye {
672                                            ssrc: source_ssrc,
673                                            reason: bye.reason,
674                                        });
675
676                                        info!("Received RTCP BYE from SSRC={:08x}", source_ssrc);
677                                    }
678                                }
679                                crate::packet::rtcp::RtcpPacket::SenderReport(sr) => {
680                                    // Process sender report
681                                    let report_ssrc = sr.ssrc;
682
683                                    debug!("Received RTCP SR from SSRC={:08x}", report_ssrc);
684
685                                    // Update stream statistics if this stream exists
686                                    if let Some(mut stream) = streams_map.get_mut(&report_ssrc) {
687                                        // Update the stream's RTCP SR info
688                                        // This will be used for calculating round-trip time
689                                        stream.update_last_sr_info(
690                                            sr.ntp_timestamp.to_u32(),
691                                            std::time::Instant::now(),
692                                        );
693
694                                        debug!(
695                                            "Updated RTCP SR info for stream SSRC={:08x}",
696                                            report_ssrc
697                                        );
698                                    }
699
700                                    // If media sync is enabled, update it
701                                    if let Some(sync) = &media_sync {
702                                        if let Ok(mut media_sync) = sync.write() {
703                                            // Update synchronization data
704                                            media_sync.update_from_sr(
705                                                report_ssrc,
706                                                sr.ntp_timestamp,
707                                                sr.rtp_timestamp,
708                                            );
709                                        }
710                                    }
711
712                                    // Emit SR event for external processing
713                                    let _ = event_tx_recv.send(RtpSessionEvent::RtcpSenderReport {
714                                        ssrc: report_ssrc,
715                                        ntp_timestamp: sr.ntp_timestamp,
716                                        rtp_timestamp: sr.rtp_timestamp,
717                                        packet_count: sr.sender_packet_count,
718                                        octet_count: sr.sender_octet_count,
719                                        report_blocks: sr.report_blocks,
720                                    });
721                                }
722                                crate::packet::rtcp::RtcpPacket::ReceiverReport(rr) => {
723                                    // Process receiver report
724                                    let report_ssrc = rr.ssrc;
725
726                                    debug!(
727                                        "Received RTCP RR from SSRC={:08x} with {} report blocks",
728                                        report_ssrc,
729                                        rr.report_blocks.len()
730                                    );
731
732                                    // If there's a report block about our SSRC, process it
733                                    for block in &rr.report_blocks {
734                                        if block.ssrc == ssrc {
735                                            debug!(
736                                                "Processing report block about our SSRC={:08x}",
737                                                ssrc
738                                            );
739
740                                            // Update session stats with packet loss info
741                                            {
742                                                let mut stats = stats_recv.lock();
743                                                stats.packets_lost = block.cumulative_lost as u64;
744
745                                                // Calculate packet loss percentage
746                                                let fraction_lost =
747                                                    block.fraction_lost as f64 / 256.0;
748                                                debug!(
749                                                    "Packet loss: {}% (fraction={})",
750                                                    fraction_lost * 100.0,
751                                                    block.fraction_lost
752                                                );
753                                            }
754                                        }
755                                    }
756
757                                    // Emit RR event for external processing
758                                    let _ =
759                                        event_tx_recv.send(RtpSessionEvent::RtcpReceiverReport {
760                                            ssrc: report_ssrc,
761                                            report_blocks: rr.report_blocks,
762                                        });
763                                }
764                                // Handle other RTCP packet types as needed
765                                _ => {
766                                    // For now, we're just logging other packet types
767                                    trace!("Received RTCP packet: {:?}", rtcp_packet);
768                                }
769                            }
770                        } else {
771                            warn!("Failed to parse RTCP packet");
772                        }
773                    }
774                    Ok(crate::traits::RtpEvent::MediaReceived {
775                        payload_type,
776                        sequence_number,
777                        timestamp,
778                        payload,
779                        source,
780                        ssrc: ssrc_from_event,
781                        marker,
782                        ..
783                    }) => {
784                        // Handle RTP packets received via transport events
785                        // This is the ONLY path for RTP packets to avoid race conditions
786
787                        // Reconstruct minimal RTP header for processing
788                        let header = RtpHeader {
789                            version: 2,
790                            padding: false,
791                            extension: false,
792                            cc: 0,
793                            marker,
794                            payload_type,
795                            sequence_number,
796                            timestamp,
797                            ssrc,
798                            csrc: vec![],
799                            extensions: None,
800                        };
801
802                        let packet = RtpPacket {
803                            header,
804                            payload: payload.clone(),
805                        };
806
807                        // Update stats
808                        {
809                            let mut session_stats = stats_recv.lock();
810                            session_stats.packets_received += 1;
811                            session_stats.bytes_received += payload.len() as u64 + 12; // payload + header
812                            session_stats.remote_addr = Some(source);
813                        }
814
815                        // Use the SSRC from the event
816                        let packet_ssrc = ssrc_from_event;
817
818                        // Get or create the stream for this SSRC. The
819                        // `entry` runs the closure exactly once per
820                        // first insert, so `created` flips iff this
821                        // packet's SSRC has never been seen — that's
822                        // also the signal for the `NewStreamDetected`
823                        // event downstream. The shard guard is dropped
824                        // before we forward the packet.
825                        let (is_new_stream, output_packet) = {
826                            let mut created = false;
827                            {
828                                let _entry = streams_map.entry(packet_ssrc).or_insert_with(|| {
829                                    created = true;
830                                    info!("New RTP stream detected with SSRC={:08x}", packet_ssrc);
831                                    RtpStream::new(packet_ssrc, clock_rate)
832                                });
833                            }
834                            (created, Some(packet.clone()))
835                        };
836
837                        // If this is a new stream, emit the NewStreamDetected event
838                        if is_new_stream {
839                            let _ = event_tx_recv
840                                .send(RtpSessionEvent::NewStreamDetected { ssrc: packet_ssrc });
841                        }
842
843                        // Forward the packet
844                        if let Some(output) = output_packet {
845                            if receive_queue_enabled {
846                                match receiver_tx.try_send(output.clone()) {
847                                    Ok(()) => {}
848                                    Err(mpsc::error::TrySendError::Full(_)) => {
849                                        trace!(
850                                            "RTP receive polling queue full; dropping duplicate packet"
851                                        );
852                                    }
853                                    Err(mpsc::error::TrySendError::Closed(_)) => {
854                                        error!(
855                                            "Failed to forward RTP packet to receiver: channel closed"
856                                        );
857                                    }
858                                }
859                            }
860
861                            // Broadcast packet received event
862                            let _ = event_tx_recv.send(RtpSessionEvent::PacketReceived(output));
863                        }
864                    }
865                    Ok(crate::traits::RtpEvent::Error(e)) => {
866                        error!("Transport error: {}", e);
867                        let _ = event_tx_recv.send(RtpSessionEvent::Error(e));
868                    }
869                    Ok(crate::traits::RtpEvent::DtmfEvent {
870                        event,
871                        end_of_event,
872                        volume,
873                        duration,
874                        timestamp,
875                        ssrc,
876                        ..
877                    }) => {
878                        // RFC 4733: forward as a typed session event so
879                        // media-core's RTP handler can bubble the digit
880                        // up to session-core without re-parsing the
881                        // 4-byte body.
882                        let _ = event_tx_recv.send(RtpSessionEvent::DtmfReceived {
883                            event,
884                            end_of_event,
885                            volume,
886                            duration,
887                            timestamp,
888                            ssrc,
889                        });
890                    }
891                    Err(e) => {
892                        debug!("Transport event channel error: {}", e);
893                    }
894                }
895            }
896        });
897
898        // Start RTCP sending task if we have a remote address and report generator
899        if let (Some(remote_addr), Some(mut rtcp_generator)) =
900            (self.config.remote_addr, self.rtcp_generator.take())
901        {
902            let transport = self.transport.clone();
903            let ssrc = self.ssrc;
904            let event_tx = self.event_tx.clone();
905            let stats = self.stats.clone();
906            let active_state = Arc::new(tokio::sync::Mutex::new(true));
907            let _active_state_clone = active_state.clone();
908            let bandwidth = self.bandwidth_bps;
909
910            // Set bandwidth in the generator
911            rtcp_generator.set_bandwidth(bandwidth);
912
913            // Start the RTCP task
914            let rtcp_task = spawn_memory_tracked("rtp_core.rtp_session.rtcp_task", async move {
915                debug!("RTCP scheduling task started");
916
917                // Initial interval calculation
918                let mut interval = rtcp_generator.calculate_interval();
919                debug!("Initial RTCP interval: {:?}", interval);
920
921                while *active_state.lock().await {
922                    // Wait for the calculated interval
923                    tokio::time::sleep(interval).await;
924
925                    // Check if we should continue
926                    if !*active_state.lock().await {
927                        break;
928                    }
929
930                    // Update RTP statistics before sending the report
931                    {
932                        let session_stats = stats.lock();
933                        rtcp_generator.update_sent_stats(
934                            session_stats.packets_sent as u32,
935                            session_stats.bytes_sent as u32,
936                        );
937
938                        // Log the current stats for debugging
939                        debug!(
940                            "Current stats for RTCP report: packets={}, bytes={}",
941                            session_stats.packets_sent, session_stats.bytes_sent
942                        );
943                    }
944
945                    // Send an RTCP report regardless of should_send_report logic for this example
946                    // We'll send a compound packet with SR and SDES
947                    debug!("Sending RTCP report");
948
949                    // Generate sender report
950                    let rtp_timestamp = std::time::SystemTime::now()
951                        .duration_since(std::time::UNIX_EPOCH)
952                        .unwrap_or_default()
953                        .as_millis() as u32;
954
955                    let sr = rtcp_generator.generate_sender_report(rtp_timestamp);
956                    let sdes = rtcp_generator.generate_sdes();
957
958                    // Create compound packet
959                    let mut compound = crate::packet::rtcp::RtcpCompoundPacket::new_with_sr(sr);
960                    compound.add_sdes(sdes);
961
962                    // Send the compound packet
963                    if let Ok(data) = compound.serialize() {
964                        if let Err(e) = transport.send_rtcp_bytes(&data, remote_addr).await {
965                            warn!("Failed to send RTCP compound packet: {}", e);
966                        } else {
967                            info!("Sent RTCP compound packet of {} bytes", data.len());
968
969                            // Emit SR event
970                            if let Some(sr) = compound.get_sr() {
971                                let _ = event_tx.send(RtpSessionEvent::RtcpSenderReport {
972                                    ssrc,
973                                    ntp_timestamp: sr.ntp_timestamp,
974                                    rtp_timestamp: sr.rtp_timestamp,
975                                    packet_count: sr.sender_packet_count,
976                                    octet_count: sr.sender_octet_count,
977                                    report_blocks: sr.report_blocks.clone(),
978                                });
979                            }
980                        }
981                    }
982
983                    // Recalculate interval for next report
984                    interval = rtcp_generator.calculate_interval();
985                    debug!("Next RTCP report in {:?}", interval);
986                }
987
988                debug!("RTCP scheduling task ended");
989            });
990
991            self.rtcp_task = Some(rtcp_task);
992        }
993
994        self.recv_task = Some(recv_task);
995        self.send_task = Some(send_task);
996        self.active = true;
997
998        info!("Started RTP session with SSRC={:08x}", ssrc);
999        Ok(())
1000    }
1001
1002    /// Send an RTP packet with payload. Now `&self` — sequence
1003    /// numbers are managed by an atomic shared with the scheduler,
1004    /// and the `sender` mpsc clone is intrinsically `Send + Sync`,
1005    /// so this no longer requires exclusive borrow. Lets concurrent
1006    /// callers (audio TX, DTMF transmitter, bridge forwarder) send
1007    /// without serialising on `Arc<Mutex<RtpSession>>`.
1008    pub async fn send_packet(
1009        &self,
1010        timestamp: RtpTimestamp,
1011        payload: Bytes,
1012        marker: bool,
1013    ) -> Result<()> {
1014        self.send_packet_with_pt(timestamp, payload, marker, self.config.payload_type)
1015            .await
1016    }
1017
1018    /// Send an RTP packet overriding the configured payload type.
1019    ///
1020    /// Needed for RFC 4733 telephone-event (DTMF) transmission — the
1021    /// session's `config.payload_type` is the audio codec PT (0/8/etc),
1022    /// but DTMF rides on a distinct PT (typically 101). All other
1023    /// fields (SSRC, marker, timestamp) follow the same rules as
1024    /// [`send_packet`](Self::send_packet).
1025    pub async fn send_packet_with_pt(
1026        &self,
1027        timestamp: RtpTimestamp,
1028        payload: Bytes,
1029        marker: bool,
1030        payload_type: u8,
1031    ) -> Result<()> {
1032        // The whole point of this method is that the caller controls
1033        // PT + timestamp explicitly — RFC 4733 telephone-event needs
1034        // every packet of a tone to share the start timestamp, and
1035        // the scheduler's `schedule_packet` would overwrite it with
1036        // its audio-rate cursor. So we bypass the scheduler's
1037        // queueing path and route directly to the sender channel.
1038        // Sequence numbers still come from the scheduler (when
1039        // present) so DTMF + audio share the seq-number space the
1040        // peer expects.
1041        let sequence = self
1042            .scheduler
1043            .as_ref()
1044            .map(|s| s.next_sequence())
1045            .unwrap_or(0);
1046        let mut header = RtpHeader::new(payload_type, sequence, timestamp, self.ssrc);
1047        header.marker = marker;
1048        let packet = RtpPacket::new(header, payload);
1049
1050        self.sender
1051            .send(packet)
1052            .await
1053            .map_err(|_| Error::SessionError("Failed to send packet".to_string()))
1054    }
1055
1056    /// Get a lock-free send handle for this session.
1057    ///
1058    /// `RtpSendHandle` is `Send + Sync + Clone` and bypasses the
1059    /// outer `Arc<Mutex<RtpSession>>` that wraps this session in
1060    /// media-core. It shares the same sequence atomic as the
1061    /// scheduler, so the wire-side sees one monotonic seq number
1062    /// space across both the audio TX path and any scheduler /
1063    /// `send_packet` call.
1064    pub fn send_handle(&self) -> Option<RtpSendHandle> {
1065        let scheduler = self.scheduler.as_ref()?;
1066        Some(RtpSendHandle {
1067            sender: self.sender.clone(),
1068            ssrc: self.ssrc,
1069            sequence: scheduler.sequence_handle(),
1070            default_payload_type: self.config.payload_type,
1071        })
1072    }
1073
1074    /// Receive an RTP packet
1075    pub async fn receive_packet(&mut self) -> Result<RtpPacket> {
1076        self.receiver
1077            .recv()
1078            .await
1079            .ok_or_else(|| Error::SessionError("Receiver channel closed".to_string()))
1080    }
1081
1082    /// Get the session statistics
1083    pub fn get_stats(&self) -> RtpSessionStats {
1084        self.stats.lock().clone()
1085    }
1086
1087    /// Get current bounded-queue occupancy for leak/perf diagnostics.
1088    pub fn queue_diagnostics(&self) -> RtpSessionQueueDiagnostics {
1089        let sender_capacity_packets = self.sender.max_capacity();
1090        let (receiver_queue_packets, receiver_capacity_packets) = if self.receive_queue_enabled {
1091            (self.receiver.len(), self.receiver.max_capacity())
1092        } else {
1093            (0, 0)
1094        };
1095        RtpSessionQueueDiagnostics {
1096            sender_queue_packets: sender_capacity_packets.saturating_sub(self.sender.capacity()),
1097            sender_capacity_packets,
1098            receiver_queue_packets,
1099            receiver_capacity_packets,
1100            event_queue_events: self.event_tx.len(),
1101            event_receiver_count: self.event_tx.receiver_count(),
1102            #[cfg(feature = "memory-diagnostics")]
1103            stream_count: self.streams.len(),
1104        }
1105    }
1106
1107    /// Set the remote address
1108    pub async fn set_remote_addr(&mut self, addr: SocketAddr) {
1109        self.config.remote_addr = Some(addr);
1110
1111        // Update stats with remote address
1112        {
1113            let mut stats = self.stats.lock();
1114            stats.remote_addr = Some(addr);
1115        }
1116
1117        // Update the transport's remote address
1118        if let Some(t) = self.transport.as_any().downcast_ref::<UdpRtpTransport>() {
1119            t.set_remote_rtp_addr(addr).await;
1120        }
1121    }
1122
1123    /// Get the local address
1124    pub fn local_addr(&self) -> Result<SocketAddr> {
1125        self.transport.local_rtp_addr()
1126    }
1127
1128    /// Get the transport
1129    pub fn transport(&self) -> Arc<dyn RtpTransport> {
1130        self.transport.clone()
1131    }
1132
1133    /// Close the session and clean up resources
1134    pub async fn close(&mut self) -> Result<()> {
1135        // Send BYE packet if we have a remote address
1136        if let Some(remote_addr) = self.config.remote_addr {
1137            // Create BYE packet
1138            let bye = crate::packet::rtcp::RtcpGoodbye::new_with_reason(
1139                self.ssrc,
1140                "Session closed".to_string(),
1141            );
1142
1143            // Create RTCP packet
1144            let rtcp_packet = crate::packet::rtcp::RtcpPacket::Goodbye(bye);
1145
1146            // Serialize and send
1147            match rtcp_packet.serialize() {
1148                Ok(data) => {
1149                    // Send using transport (through RTCP port if available)
1150                    if let Err(e) = self.transport.send_rtcp_bytes(&data, remote_addr).await {
1151                        warn!("Failed to send RTCP BYE: {}", e);
1152                    }
1153                }
1154                Err(e) => {
1155                    warn!("Failed to serialize RTCP BYE: {}", e);
1156                }
1157            }
1158        }
1159
1160        // Stop the scheduler if running
1161        if let Some(scheduler) = &mut self.scheduler {
1162            scheduler.stop().await;
1163        }
1164
1165        // Stop the receive task
1166        if let Some(handle) = self.recv_task.take() {
1167            handle.abort();
1168            let _ = handle.await;
1169        }
1170
1171        // Stop the send task
1172        if let Some(handle) = self.send_task.take() {
1173            handle.abort();
1174            let _ = handle.await;
1175        }
1176
1177        // Stop the RTCP task
1178        if let Some(handle) = self.rtcp_task.take() {
1179            handle.abort();
1180            let _ = handle.await;
1181        }
1182
1183        // Close the transport
1184        let _ = self.transport.close().await;
1185
1186        self.active = false;
1187        info!("Closed RTP session with SSRC={:08x}", self.ssrc);
1188
1189        Ok(())
1190    }
1191
1192    /// Get the current timestamp
1193    pub fn get_timestamp(&self) -> RtpTimestamp {
1194        if let Some(scheduler) = &self.scheduler {
1195            scheduler.get_timestamp()
1196        } else {
1197            // Generate based on uptime if no scheduler
1198            let now = std::time::SystemTime::now();
1199            let since_epoch = now
1200                .duration_since(std::time::UNIX_EPOCH)
1201                .unwrap_or_else(|_| Duration::from_secs(0));
1202
1203            let secs = since_epoch.as_secs();
1204            let nanos = since_epoch.subsec_nanos();
1205
1206            // Convert to timestamp units (samples)
1207            let timestamp_secs = secs * (self.config.clock_rate as u64);
1208            let timestamp_fraction =
1209                ((nanos as u64) * (self.config.clock_rate as u64)) / 1_000_000_000;
1210
1211            (timestamp_secs + timestamp_fraction) as u32
1212        }
1213    }
1214
1215    /// Current RTP timestamp cursor — the timestamp the next audio
1216    /// packet would carry. Coherent with the audio stream's SSRC per
1217    /// RFC 4733 §2.1: telephone-event packets share the start
1218    /// timestamp of the surrounding audio so receivers can align
1219    /// tones with the audio they overlay.
1220    ///
1221    /// The implementation derives the timestamp from wall-clock at
1222    /// the configured clock rate rather than reading the scheduler's
1223    /// internal `self.timestamp` field directly. This matters because:
1224    ///
1225    /// - When audio packets are flowing through the scheduler at the
1226    ///   audio rate, wall-clock and scheduler cursor stay in lockstep
1227    ///   (both advance at `clock_rate` Hz), so the returned value is
1228    ///   audio-anchored as RFC 4733 expects.
1229    /// - When no audio is flowing (e.g. the streampeer/dtmf example,
1230    ///   which exercises only RTP-control with PT 101 and never
1231    ///   pushes a PCMU audio source), the scheduler's `self.timestamp`
1232    ///   is frozen. A frozen timestamp would collapse successive DTMF
1233    ///   tones into one `(peer, ssrc, ts)` dedup key at the receiver,
1234    ///   silently dropping every digit after the first. Wall-clock
1235    ///   keeps successive tones distinct unconditionally.
1236    pub fn current_timestamp(&self) -> RtpTimestamp {
1237        let now = std::time::SystemTime::now();
1238        let since_epoch = now
1239            .duration_since(std::time::UNIX_EPOCH)
1240            .unwrap_or_else(|_| Duration::from_secs(0));
1241        let secs = since_epoch.as_secs();
1242        let nanos = since_epoch.subsec_nanos();
1243        let timestamp_secs = secs * (self.config.clock_rate as u64);
1244        let timestamp_fraction = ((nanos as u64) * (self.config.clock_rate as u64)) / 1_000_000_000;
1245        (timestamp_secs + timestamp_fraction) as u32
1246    }
1247
1248    /// Get the SSRC of this session
1249    pub fn get_ssrc(&self) -> RtpSsrc {
1250        self.ssrc
1251    }
1252
1253    /// Subscribe to session events
1254    pub fn subscribe(&self) -> broadcast::Receiver<RtpSessionEvent> {
1255        self.event_tx.subscribe()
1256    }
1257
1258    /// Get the current payload type
1259    pub fn get_payload_type(&self) -> u8 {
1260        self.config.payload_type
1261    }
1262
1263    /// Set the payload type
1264    pub fn set_payload_type(&mut self, payload_type: u8) {
1265        self.config.payload_type = payload_type;
1266    }
1267
1268    /// Get a stream by SSRC, if it exists
1269    pub async fn get_stream(&self, ssrc: RtpSsrc) -> Option<RtpStreamStats> {
1270        self.streams.get(&ssrc).map(|stream| stream.get_stats())
1271    }
1272
1273    /// Get a list of all current streams
1274    pub async fn get_all_streams(&self) -> Vec<RtpStreamStats> {
1275        self.streams
1276            .iter()
1277            .map(|entry| entry.value().get_stats())
1278            .collect()
1279    }
1280
1281    /// Get the number of active streams
1282    pub async fn stream_count(&self) -> usize {
1283        self.streams.len()
1284    }
1285
1286    /// Get a list of all SSRCs known to this session
1287    ///
1288    /// This returns all SSRCs that have been seen, even if their streams
1289    /// haven't released any packets from their jitter buffers yet.
1290    pub async fn get_all_ssrcs(&self) -> Vec<RtpSsrc> {
1291        self.streams.iter().map(|entry| *entry.key()).collect()
1292    }
1293
1294    /// Force creation of a stream for a specific SSRC
1295    ///
1296    /// This is useful when we want to ensure a stream exists for an SSRC
1297    /// even if no packets have been received yet.
1298    pub async fn create_stream_for_ssrc(&mut self, ssrc: RtpSsrc) -> bool {
1299        // Check if this SSRC already exists. The contains_key + insert
1300        // pair has a benign race (two callers may both decide "new" and
1301        // race the insert), but we only need a stable per-SSRC entry —
1302        // DashMap's `entry()` arbitrates.
1303        if self.streams.contains_key(&ssrc) {
1304            debug!("Stream for SSRC={:08x} already exists", ssrc);
1305            return false;
1306        }
1307
1308        // Create the stream
1309        info!("Manually creating new RTP stream for SSRC={:08x}", ssrc);
1310        let stream = if self.config.enable_jitter_buffer {
1311            debug!("Creating stream with jitter buffer for SSRC={:08x}", ssrc);
1312            RtpStream::with_jitter_buffer(
1313                ssrc,
1314                self.config.clock_rate,
1315                self.config.jitter_buffer_size.unwrap_or(50),
1316                self.config.max_packet_age_ms.unwrap_or(200) as u64,
1317            )
1318        } else {
1319            debug!(
1320                "Creating stream without jitter buffer for SSRC={:08x}",
1321                ssrc
1322            );
1323            RtpStream::new(ssrc, self.config.clock_rate)
1324        };
1325
1326        // The contains_key check above is racy w.r.t. the recv hot
1327        // path also inserting on first packet; `entry()` arbitrates.
1328        // The closure runs only on first insert, so `closure_ran`
1329        // tells us whether *we* created the entry or lost the race.
1330        let mut closure_ran = false;
1331        {
1332            let _entry = self.streams.entry(ssrc).or_insert_with(|| {
1333                closure_ran = true;
1334                stream
1335            });
1336        }
1337        if !closure_ran {
1338            return false;
1339        }
1340
1341        // Emit the new stream event
1342        debug!("Emitting NewStreamDetected event for SSRC={:08x}", ssrc);
1343        let _ = self
1344            .event_tx
1345            .send(RtpSessionEvent::NewStreamDetected { ssrc });
1346
1347        true
1348    }
1349
1350    /// Send an RTCP BYE packet to notify that we're leaving the session
1351    ///
1352    /// This can be used to notify other participants that we're leaving the session
1353    /// without closing the entire RtpSession. The BYE packet includes our SSRC and
1354    /// an optional reason string.
1355    ///
1356    /// Returns an error if serialization fails or if there's no remote address configured.
1357    pub async fn send_bye(&self, reason: Option<String>) -> Result<()> {
1358        // Check if we have a remote address
1359        let remote_addr = match self.config.remote_addr {
1360            Some(addr) => addr,
1361            None => {
1362                return Err(Error::SessionError(
1363                    "No remote address configured".to_string(),
1364                ))
1365            }
1366        };
1367
1368        // Create BYE packet
1369        let bye = crate::packet::rtcp::RtcpGoodbye::new_with_reason(
1370            self.ssrc,
1371            reason.unwrap_or_else(|| "Session terminated".to_string()),
1372        );
1373
1374        // Create RTCP packet
1375        let rtcp_packet = crate::packet::rtcp::RtcpPacket::Goodbye(bye);
1376
1377        // Serialize and send
1378        match rtcp_packet.serialize() {
1379            Ok(data) => {
1380                // Send using transport
1381                self.transport.send_rtcp_bytes(&data, remote_addr).await
1382            }
1383            Err(e) => Err(Error::SerializationError(format!(
1384                "Failed to serialize RTCP BYE: {}",
1385                e
1386            ))),
1387        }
1388    }
1389
1390    /// Send an RTCP Sender Report (SR) packet
1391    ///
1392    /// A Sender Report contains:
1393    /// - Our SSRC
1394    /// - Current NTP and RTP timestamps
1395    /// - Packet and octet counts
1396    /// - Optional report blocks with reception statistics about other sources
1397    ///
1398    /// This method generates an SR based on the current session statistics, which is useful
1399    /// for providing quality metrics to other participants.
1400    ///
1401    /// Returns an error if serialization fails or if there's no remote address configured.
1402    pub async fn send_sender_report(&self) -> Result<()> {
1403        // Check if we have a remote address
1404        let remote_addr = match self.config.remote_addr {
1405            Some(addr) => addr,
1406            None => {
1407                return Err(Error::SessionError(
1408                    "No remote address configured".to_string(),
1409                ))
1410            }
1411        };
1412
1413        // Get session stats
1414        let session_stats = self.stats.lock().clone();
1415
1416        // Create a new SR packet
1417        let mut sr = crate::packet::rtcp::RtcpSenderReport::new(self.ssrc);
1418
1419        // Set current NTP timestamp
1420        sr.ntp_timestamp = crate::packet::rtcp::NtpTimestamp::now();
1421
1422        // Set current RTP timestamp (convert from NTP time)
1423        sr.rtp_timestamp = self.get_timestamp();
1424
1425        // Set packet and octet count from session stats
1426        sr.sender_packet_count = session_stats.packets_sent as u32;
1427        sr.sender_octet_count = session_stats.bytes_sent as u32;
1428
1429        // Add report blocks for active streams (remote SSRCs we're receiving from)
1430        // Up to 31 streams per RTCP packet.
1431        for entry in self.streams.iter().take(31) {
1432            let ssrc = *entry.key();
1433            let stream_stats = entry.value().get_stats();
1434
1435            // Create a report block for this source
1436            let mut block = crate::packet::rtcp::RtcpReportBlock::new(ssrc);
1437
1438            // Set statistics
1439            let expected_packets = stream_stats.highest_seq - stream_stats.first_seq + 1;
1440            let (fraction_lost, cumulative_lost) =
1441                block.calculate_packet_loss(expected_packets, stream_stats.received);
1442
1443            block.fraction_lost = fraction_lost;
1444            block.cumulative_lost = cumulative_lost as u32;
1445            block.highest_seq = stream_stats.highest_seq;
1446            block.jitter = stream_stats.jitter;
1447
1448            // TODO: Set last_sr and delay_since_last_sr when we process incoming SRs
1449
1450            // Add the block to the SR
1451            sr.add_report_block(block);
1452        }
1453
1454        // **FIX: Update our own MediaSync context with the SR data we're sending**
1455        // This ensures our own timing data flows into MediaSync for API access
1456        if let Some(media_sync) = &self.media_sync {
1457            if let Ok(mut sync) = media_sync.write() {
1458                sync.update_from_sr(self.ssrc, sr.ntp_timestamp, sr.rtp_timestamp);
1459                debug!(
1460                    "Updated MediaSync with our own SR: SSRC={:08x}, NTP={:?}, RTP={}",
1461                    self.ssrc, sr.ntp_timestamp, sr.rtp_timestamp
1462                );
1463            }
1464        }
1465
1466        // Create RTCP packet
1467        let rtcp_packet = crate::packet::rtcp::RtcpPacket::SenderReport(sr);
1468
1469        // Serialize and send
1470        match rtcp_packet.serialize() {
1471            Ok(data) => self.transport.send_rtcp_bytes(&data, remote_addr).await,
1472            Err(e) => Err(Error::SerializationError(format!(
1473                "Failed to serialize RTCP SR: {}",
1474                e
1475            ))),
1476        }
1477    }
1478
1479    /// Send an RTCP Receiver Report (RR) packet
1480    ///
1481    /// A Receiver Report contains:
1482    /// - Our SSRC
1483    /// - Report blocks with reception statistics about other sources
1484    ///
1485    /// This method generates an RR based on the current stream statistics, which is useful
1486    /// for providing quality metrics to other participants when we're receiving but not sending.
1487    ///
1488    /// Returns an error if serialization fails or if there's no remote address configured.
1489    pub async fn send_receiver_report(&self) -> Result<()> {
1490        // Check if we have a remote address
1491        let remote_addr = match self.config.remote_addr {
1492            Some(addr) => addr,
1493            None => {
1494                return Err(Error::SessionError(
1495                    "No remote address configured".to_string(),
1496                ))
1497            }
1498        };
1499
1500        // Create a new RR packet
1501        let mut rr = crate::packet::rtcp::RtcpReceiverReport::new(self.ssrc);
1502
1503        // Add report blocks for active streams (remote SSRCs we're receiving from)
1504        // Up to 31 streams per RTCP packet.
1505        for entry in self.streams.iter().take(31) {
1506            let ssrc = *entry.key();
1507            let stream_stats = entry.value().get_stats();
1508
1509            // Create a report block for this source
1510            let mut block = crate::packet::rtcp::RtcpReportBlock::new(ssrc);
1511
1512            // Set statistics
1513            let expected_packets = stream_stats.highest_seq - stream_stats.first_seq + 1;
1514            let (fraction_lost, cumulative_lost) =
1515                block.calculate_packet_loss(expected_packets, stream_stats.received);
1516
1517            block.fraction_lost = fraction_lost;
1518            block.cumulative_lost = cumulative_lost as u32;
1519            block.highest_seq = stream_stats.highest_seq;
1520            block.jitter = stream_stats.jitter;
1521
1522            // TODO: Set last_sr and delay_since_last_sr when we process incoming SRs
1523
1524            // Add the block to the RR
1525            rr.add_report_block(block);
1526        }
1527
1528        // Create RTCP packet
1529        let rtcp_packet = crate::packet::rtcp::RtcpPacket::ReceiverReport(rr);
1530
1531        // Serialize and send
1532        match rtcp_packet.serialize() {
1533            Ok(data) => self.transport.send_rtcp_bytes(&data, remote_addr).await,
1534            Err(e) => Err(Error::SerializationError(format!(
1535                "Failed to serialize RTCP RR: {}",
1536                e
1537            ))),
1538        }
1539    }
1540
1541    /// Enable media synchronization
1542    pub fn enable_media_sync(&mut self) -> Arc<std::sync::RwLock<crate::sync::MediaSync>> {
1543        let sync = Arc::new(std::sync::RwLock::new(crate::sync::MediaSync::new()));
1544        self.media_sync = Some(sync.clone());
1545
1546        // Register our stream
1547        if let Ok(mut media_sync) = sync.write() {
1548            media_sync.register_stream(self.ssrc, self.config.clock_rate);
1549        }
1550
1551        sync
1552    }
1553
1554    /// Get the media synchronization context
1555    pub fn media_sync(&self) -> Option<Arc<std::sync::RwLock<crate::sync::MediaSync>>> {
1556        self.media_sync.clone()
1557    }
1558
1559    /// Set the session bandwidth in bits per second
1560    ///
1561    /// This affects the RTCP report interval calculation.
1562    /// Higher bandwidth means more frequent RTCP packets.
1563    pub fn set_bandwidth(&mut self, bandwidth_bps: u32) {
1564        self.bandwidth_bps = bandwidth_bps;
1565    }
1566
1567    /// Create a sender handle for this session
1568    ///
1569    /// This creates a lightweight handle that can be used to send RTP packets
1570    /// from another thread. This is useful when you need to send packets
1571    /// but don't want to clone the entire session.
1572    pub fn create_sender_handle(&self) -> RtpSessionSender {
1573        RtpSessionSender {
1574            sender: self.sender.clone(),
1575            ssrc: self.ssrc,
1576            payload_type: self.config.payload_type,
1577            clock_rate: self.config.clock_rate,
1578        }
1579    }
1580
1581    /// Get the UDP socket handle from the transport
1582    ///
1583    /// This method is used to access the underlying UDP socket when needed for
1584    /// other protocols that need to share the same socket (e.g., DTLS).
1585    pub async fn get_socket_handle(&self) -> Result<Arc<UdpSocket>> {
1586        // Try to get the socket from the UdpRtpTransport
1587        if let Some(t) = self.transport.as_any().downcast_ref::<UdpRtpTransport>() {
1588            // Clone and return the RTP socket using the public method
1589            let socket = t.get_socket();
1590            return Ok(socket);
1591        }
1592
1593        // If we get here, the transport is not UdpRtpTransport
1594        Err(Error::Transport(
1595            "Transport is not a UDP transport".to_string(),
1596        ))
1597    }
1598}
1599
1600/// A lightweight sender handle for an RTP session
1601///
1602/// This handle can be used to send RTP packets to the session
1603/// from another thread without having to clone the entire session.
1604#[derive(Clone)]
1605#[allow(dead_code)] // retained (liveness/Drop hold or reserved); not read
1606pub struct RtpSessionSender {
1607    /// Channel for sending packets
1608    sender: mpsc::Sender<RtpPacket>,
1609
1610    /// SSRC for this session
1611    ssrc: RtpSsrc,
1612
1613    /// Payload type
1614    payload_type: u8,
1615
1616    /// Clock rate for the payload type
1617    #[allow(dead_code)] // retained (liveness/Drop hold or reserved); not read
1618    clock_rate: u32,
1619}
1620
1621impl RtpSessionSender {
1622    /// Send an RTP packet with payload
1623    pub async fn send_packet(
1624        &self,
1625        timestamp: RtpTimestamp,
1626        payload: Bytes,
1627        marker: bool,
1628    ) -> Result<()> {
1629        // Create RTP header
1630        let mut header = RtpHeader::new(
1631            self.payload_type,
1632            0, // Sequence number will be set by scheduler
1633            timestamp,
1634            self.ssrc,
1635        );
1636
1637        // Set marker bit if needed
1638        header.marker = marker;
1639
1640        // Create packet
1641        let packet = RtpPacket::new(header, payload);
1642
1643        // Send the packet
1644        self.sender
1645            .send(packet)
1646            .await
1647            .map_err(|_| Error::SessionError("Failed to send packet".to_string()))
1648    }
1649}
1650
1651#[cfg(test)]
1652mod tests {
1653    use super::*;
1654
1655    #[test]
1656    fn default_session_buffer_config_preserves_channel_capacities() {
1657        let config = RtpSessionConfig::default();
1658
1659        assert_eq!(
1660            config.session_buffer_config.sender_channel_capacity,
1661            RTP_SESSION_CHANNEL_CAPACITY
1662        );
1663        assert_eq!(
1664            config.session_buffer_config.receiver_channel_capacity,
1665            RTP_SESSION_RECEIVE_QUEUE_CAPACITY
1666        );
1667        assert_eq!(
1668            config.session_buffer_config.event_channel_capacity,
1669            RTP_SESSION_CHANNEL_CAPACITY
1670        );
1671        assert_eq!(
1672            config.transport_buffer_config,
1673            RtpTransportBufferConfig::default()
1674        );
1675    }
1676}