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}