rvoip-rtp-core 0.2.3

RTP/RTCP protocol implementation for the rvoip stack
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
//! RTP Core library for the RVOIP project
//!
//! This crate provides RTP packet encoding/decoding, RTCP support,
//! and other utilities for handling real-time media transport.
//!
//! The library is organized into several modules:
//!
//! - `packet`: RTP and RTCP packet definitions and processing
//! - `session`: RTP session management including SSRC demultiplexing
//! - `transport`: Network transport for RTP/RTCP
//! - `srtp`: Secure RTP implementation
//! - `stats`: RTP statistics collection
//! - `time`: Timing and clock utilities
//! - `sync`: Media synchronization for multiple streams
//! - `traits`: Public traits for integration with other crates
//! - `payload`: RTP payload format handlers
//! - `buffer`: High-performance buffer management for receiving and transmitting packets
//! - `csrc`: CSRC management
//! - `error`: Error handling
//! - `rtcp`: RTCP packet definitions and processing
//! - `dtls`: DTLS support
//! - `api`: New API module with client/server separation
//! - `feedback`: Advanced RTCP feedback mechanisms for real-time adaptation
//!
//! ## New API Structure
//!
//! The `api` module provides a higher-level interface with clear client/server separation:
//!
//! - `api::client`: Client-side media transport for sending/receiving media frames
//! - `api::server`: Server-side media transport for handling multiple clients
//! - `api::common`: Shared types and utilities used by both client and server
//!
//! This structure makes the library easier to use for higher-level components like media-core.
//!
//! ## Advanced RTCP Feedback
//!
//! The `feedback` module provides intelligent RTCP feedback generation for optimal media quality:
//!
//! - Picture Loss Indication (PLI) and Full Intra Request (FIR) for video recovery
//! - Receiver Estimated Max Bitrate (REMB) for bandwidth adaptation
//! - Transport-wide Congestion Control feedback for network optimization
//! - Google Congestion Control (GCC) algorithm implementation
//! - Quality-based feedback decisions using multiple network metrics
//! - Configurable feedback generation with rate limiting and priority handling
//!
//! This enables WebRTC-compatible adaptive streaming with automatic quality optimization.
//!
//! ## Buffer Management
//!
//! The `buffer` module provides optimized memory and packet management
//! for high-scale deployments:
//!
//! - Memory pooling to reduce allocations
//! - Adaptive jitter buffer for handling network variation
//! - Priority-based transmit buffering
//! - Congestion control for network adaptation
//! - Global memory limits to prevent OOM conditions
//! - Efficient packet ordering and scheduling
//!
//! This is ideal for deployments handling tens of thousands of concurrent streams.

mod error;

// Main modules
pub mod packet;
pub mod session;
pub mod srtp;
pub mod stats;
pub mod time;
pub mod traits;
pub mod transport;
// payload module moved to media-core
pub mod api;
pub mod buffer;
pub mod csrc;
pub mod dtls;
pub mod events;
pub mod feedback;
pub mod network;
pub mod rtcp;
pub mod security;
pub mod sync;

/// The default maximum size for RTP packets in bytes
pub const DEFAULT_MAX_PACKET_SIZE: usize = 1500;

/// Typedef for RTP timestamp values
pub type RtpTimestamp = u32;

/// Typedef for RTP sequence numbers
pub type RtpSequenceNumber = u16;

/// Typedef for RTP synchronization source identifier
pub type RtpSsrc = u32;

/// Typedef for RTP contributing source identifier
pub type RtpCsrc = u32;

/// Result type for RTP operations
pub type Result<T> = std::result::Result<T, Error>;

// Re-export core types
pub use error::Error;

// Re-export common types from packet module
pub use packet::extension::{
    ids::AUDIO_LEVEL, ids::FRAME_MARKING, ids::SDES, ids::TRANSPORT_CC, ids::VIDEO_ORIENTATION,
    uris::ABS_SEND_TIME, uris::MID, uris::REPAIR_RTP_STREAM_ID, uris::STREAM_ID,
    uris::VIDEO_CONTENT_TYPE, ExtensionElement, RtpHeaderExtensions,
};
pub use packet::header::RtpHeader;
pub use packet::rtcp::{
    NtpTimestamp, RtcpApplicationDefined, RtcpCompoundPacket, RtcpExtendedReport, RtcpGoodbye,
    RtcpPacket, RtcpReceiverReport, RtcpReportBlock, RtcpSenderReport, RtcpSourceDescription,
    RtcpXrBlock, VoipMetricsBlock,
};
pub use packet::rtp::RtpPacket;

// Re-export session types
pub use session::{
    RtpSendHandle, RtpSession, RtpSessionBufferConfig, RtpSessionConfig, RtpSessionEvent,
    RtpSessionStats, RtpStream, RtpStreamStats,
};

// Re-export transport types
pub use transport::{RtpTransport, RtpTransportBufferConfig, RtpTransportConfig, UdpRtpTransport};

// Re-export traits for media-core integration
pub use traits::media_transport::RtpMediaTransport;
pub use traits::{MediaTransport, RtpEvent};

// Payload format types moved to media-core
// Use media_core::rtp_processing::payload instead

pub use csrc::{CsrcManager, CsrcMapping, MAX_CSRC_COUNT};

// Re-export sync utilities
pub use sync::clock::MediaClock;
pub use sync::mapping::TimestampMapper;
pub use sync::MediaSync;

// Re-export feedback types for RTCP feedback mechanisms
pub use feedback::packets::{
    FeedbackPacket, FirPacket, PliPacket, RembPacket, SliPacket, TransportCcPacket, TstoPacket,
    RTCP_HEADER_SIZE,
};
pub use feedback::{
    CongestionState, FeedbackConfig, FeedbackContext, FeedbackDecision, FeedbackGenerator,
    FeedbackGeneratorFactory, FeedbackPriority, QualityDegradation,
};
// Feedback generators and algorithms moved to media-core
// Use media_core::rtp_processing::rtcp instead

// Re-export the new API components for easier access
pub use api::client::{ClientConfig, ClientConfigBuilder, ClientFactory, MediaTransportClient};
pub use api::common::buffer::{BufferStats, MediaBuffer, MediaBufferConfig};
pub use api::common::config::{
    BaseTransportConfig, NetworkPreset, SecurityInfo, SecurityMode, SrtpProfile,
};
pub use api::common::error::{BufferError, MediaTransportError, SecurityError, StatsError};
pub use api::common::events::{MediaEventCallback, MediaTransportEvent};
pub use api::common::frame::{MediaFrame, MediaFrameType};
pub use api::common::stats::{MediaStats, QualityLevel};
pub use api::server::{
    ClientInfo, MediaTransportServer, ServerConfig, ServerConfigBuilder, ServerFactory,
};

/// Prelude module with commonly used types
pub mod prelude {
    pub use crate::{
        Error, Result, RtpCsrc, RtpHeader, RtpPacket, RtpSequenceNumber, RtpSession,
        RtpSessionConfig, RtpSsrc, RtpTimestamp,
    };

    pub use crate::packet::rtcp::{
        NtpTimestamp, RtcpPacket, RtcpReceiverReport, RtcpReportBlock, RtcpSenderReport,
    };

    pub use crate::traits::{MediaTransport, RtpMediaTransport};

    // Add new API types to prelude for easy access
    pub use crate::api::client::{ClientConfig, ClientFactory, MediaTransportClient};
    pub use crate::api::common::events::{MediaEventCallback, MediaTransportEvent};
    pub use crate::api::common::frame::{MediaFrame, MediaFrameType};
    pub use crate::api::server::{ClientInfo, MediaTransportServer, ServerConfig, ServerFactory};
}

#[cfg(test)]
mod tests {
    use super::*;
    use bytes::Bytes;
    use packet::{hex_dump, RtpHeader};
    use tracing::debug;

    // Set up a simple test logger
    fn init_test_logging() {
        let _ = tracing_subscriber::fmt()
            .with_max_level(tracing::Level::DEBUG)
            .try_init();
    }

    #[test]
    fn test_rtp_header_serialize_parse() {
        init_test_logging();

        // Create a simple RTP header
        let original_header = RtpHeader::new(96, 1000, 0x12345678, 0xabcdef01);
        debug!("Original header: PT={}", original_header.payload_type);

        // Serialize the header
        let mut buf = bytes::BytesMut::with_capacity(12);
        original_header.serialize(&mut buf).unwrap();

        // Debug serialized buffer
        debug!("Serialized header bytes: [{}]", hex_dump(&buf));

        // Convert to bytes
        let buf = buf.freeze();

        // Parse the header back
        let mut reader = buf.clone();
        let parsed_header = RtpHeader::parse(&mut reader).unwrap();
        debug!("Parsed header: PT={}", parsed_header.payload_type);

        // Verify fields
        assert_eq!(parsed_header.version, 2);
        assert_eq!(
            parsed_header.payload_type, 96,
            "Payload type mismatch: expected 96, got {}",
            parsed_header.payload_type
        );
        assert_eq!(parsed_header.sequence_number, 1000);
        assert_eq!(parsed_header.timestamp, 0x12345678);
        assert_eq!(parsed_header.ssrc, 0xabcdef01);
        assert_eq!(parsed_header.padding, false);
        assert_eq!(parsed_header.extension, false);
        assert_eq!(parsed_header.cc, 0);
        assert_eq!(parsed_header.marker, false);
    }

    #[test]
    fn test_rtp_packet_serialize_parse() {
        // Create payload
        let payload_data = b"test payload data";
        let payload = Bytes::copy_from_slice(payload_data);

        // Create a packet
        let original_packet = RtpPacket::new_with_payload(
            96,         // Payload type
            1000,       // Sequence number
            0x12345678, // Timestamp
            0xabcdef01, // SSRC
            payload.clone(),
        );

        // Serialize the packet
        let serialized = original_packet.serialize().unwrap();

        // Parse it back
        let parsed_packet = RtpPacket::parse(&serialized).unwrap();

        // Verify fields
        assert_eq!(parsed_packet.header.version, 2);
        assert_eq!(parsed_packet.header.payload_type, 96);
        assert_eq!(parsed_packet.header.sequence_number, 1000);
        assert_eq!(parsed_packet.header.timestamp, 0x12345678);
        assert_eq!(parsed_packet.header.ssrc, 0xabcdef01);
        assert_eq!(parsed_packet.payload, payload);
    }

    #[test]
    fn test_rtp_header_with_csrc() {
        // Create header with CSRC list
        let mut header = RtpHeader::new(96, 1000, 0x12345678, 0xabcdef01);
        header.csrc = vec![0x11111111, 0x22222222];
        header.cc = 2;

        // Serialize the header
        let mut buf = bytes::BytesMut::with_capacity(20);
        header.serialize(&mut buf).unwrap();

        // Parse it back
        let mut reader = buf.freeze();
        let parsed_header = RtpHeader::parse(&mut reader).unwrap();

        // Verify fields
        assert_eq!(parsed_header.cc, 2);
        assert_eq!(parsed_header.csrc.len(), 2);
        assert_eq!(parsed_header.csrc[0], 0x11111111);
        assert_eq!(parsed_header.csrc[1], 0x22222222);
    }

    #[test]
    fn test_rtp_header_with_extension() {
        init_test_logging();

        // Create header with extension
        let mut header = RtpHeader::new(96, 1000, 0x12345678, 0xabcdef01);
        header.extension = true;

        // Create extensions with legacy format (0x1234 profile ID)
        let mut ext = RtpHeaderExtensions::new_legacy(0x1234);
        // Add a single extension element with the extension data
        ext.elements.push(ExtensionElement {
            id: 1, // Any ID for legacy format
            data: Bytes::from_static(b"extension data"),
        });
        header.extensions = Some(ext);

        debug!(
            "Original header with extension: ext={}, format={:?}, data_len={:?}",
            header.extension,
            header.extensions.as_ref().map(|e| e.format),
            header.extensions.as_ref().map(|e| e
                .elements
                .iter()
                .map(|el| el.data.len())
                .sum::<usize>())
        );

        // Serialize the header
        let mut buf = bytes::BytesMut::with_capacity(40);
        header.serialize(&mut buf).unwrap();
        debug!(
            "Serialized extension header (size={}): [{}]",
            buf.len(),
            hex_dump(&buf)
        );

        // Directly check if extension bit is correctly set in serialized data
        let first_byte = buf[0];
        debug!(
            "First byte: 0x{:02x}, extension bit set: {}",
            first_byte,
            ((first_byte >> 4) & 0x01) == 1
        );

        // Manually parse first byte to make sure our bit positions are correct
        let version = (first_byte >> 6) & 0x03;
        let padding = ((first_byte >> 5) & 0x01) == 1;
        let extension = ((first_byte >> 4) & 0x01) == 1;
        let cc = first_byte & 0x0F;
        debug!(
            "Manual parse of first byte 0x{:02x}: V={}, P={}, X={}, CC={}",
            first_byte, version, padding, extension, cc
        );

        // Parse it back with our parser
        let mut reader = buf.freeze();
        debug!("Buffer size for parsing: {}", reader.len());

        let parse_result = RtpHeader::parse(&mut reader);
        if let Err(ref e) = parse_result {
            debug!("Parse error: {:?}", e);
        }

        let parsed_header = parse_result.unwrap();
        debug!("Remaining bytes after parse: {}", reader.len());

        // Verify fields
        assert_eq!(parsed_header.extension, true);
        assert!(parsed_header.extensions.is_some());

        let parsed_extensions = parsed_header.extensions.unwrap();
        assert_eq!(parsed_extensions.profile_id, 0x1234);
        assert!(!parsed_extensions.elements.is_empty());

        // Get the parsed extension data
        let parsed_data = &parsed_extensions.elements[0].data;
        let original_data = b"extension data";

        // Verify that the parsed data contains the original data
        assert!(
            parsed_data.starts_with(original_data),
            "Extension data doesn't match. Expected to start with: {:?}, got: {:?}",
            original_data,
            parsed_data
        );
    }

    #[test]
    fn test_parse_real_world_packet() {
        init_test_logging();

        // This is the hex data from a typical RTP packet:
        // First byte: 0x80 = Version 2, no padding, no extension, 0 CSRCs
        // Second byte: 0x00 = No marker, PT 0 (PCMU/G.711)
        let packet_data = [
            0x80, 0x00, 0xfd, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0x65,
            0x73, 0x74,
        ];

        debug!("Test packet data: [{}]", hex_dump(&packet_data));

        // Try to parse the RTP header directly first
        let mut buf = Bytes::copy_from_slice(&packet_data);
        let header_result = packet::RtpHeader::parse(&mut buf);

        if let Err(ref e) = header_result {
            debug!("RTP header parse failed: {:?}", e);
        } else {
            debug!("RTP header parse succeeded, remaining bytes: {}", buf.len());
        }

        assert!(
            header_result.is_ok(),
            "Failed to parse RTP header: {:?}",
            header_result.err()
        );

        // Now try to parse the full packet
        let packet_result = RtpPacket::parse(&packet_data);

        if let Err(ref e) = packet_result {
            debug!("RTP packet parse failed: {:?}", e);
        } else {
            debug!("RTP packet parse succeeded");
        }

        assert!(
            packet_result.is_ok(),
            "Failed to parse RTP packet: {:?}",
            packet_result.err()
        );

        let parsed = packet_result.unwrap();

        // Verify header fields based on the hex data
        assert_eq!(parsed.header.version, 2); // 0x80 -> version 2
        assert_eq!(parsed.header.payload_type, 0); // 0x00 -> PT 0
        assert_eq!(parsed.header.cc, 0); // 0x80 -> 0 CSRCs
        assert_eq!(parsed.header.sequence_number, 0xfd70); // Sequence from bytes 2-3
        assert_eq!(parsed.header.timestamp, 0); // Timestamp from bytes 4-7
        assert_eq!(parsed.header.ssrc, 0); // SSRC from bytes 8-11

        // The payload should be "Test"
        assert_eq!(parsed.payload.len(), 4);
        assert_eq!(parsed.payload.as_ref(), &b"Test"[..]);
    }

    #[test]
    fn test_serialize_rtp_packet_with_extension() {
        // Create a header with extension
        let mut header = RtpHeader::new(96, 1000, 12345, 0xABCDEF01);
        header.extension = true;

        // Create extensions with legacy format (0x1234 profile ID)
        let mut ext = RtpHeaderExtensions::new_legacy(0x1234);
        // Add a single extension element with the extension data
        ext.elements.push(ExtensionElement {
            id: 1, // Any ID for legacy format
            data: Bytes::from_static(b"extension data"),
        });
        header.extensions = Some(ext);

        println!(
            "Extension: {}, profile ID: {}, Data length: {}",
            header.extension,
            header
                .extensions
                .as_ref()
                .map(|e| e.profile_id)
                .unwrap_or(0),
            header
                .extensions
                .as_ref()
                .map(|e| e.elements.iter().map(|el| el.data.len()).sum::<usize>())
                .unwrap_or(0)
        );

        // Create packet
        let payload = Bytes::from_static(b"test payload");
        let packet = RtpPacket::new(header, payload);

        // Serialize
        let bytes = packet.serialize().unwrap();

        // Should have extension flag set in header
        assert_eq!(bytes[0] & 0x10, 0x10);

        // Extension header offset: 12 (fixed header) + 0 (no CSRCs)
        let ext_header_offset = 12;

        // Extension header: defined ID (16 bits) + length in 32-bit words (16 bits)
        let ext_id =
            ((bytes[ext_header_offset] as u16) << 8) | (bytes[ext_header_offset + 1] as u16);
        let ext_len_words =
            ((bytes[ext_header_offset + 2] as u16) << 8) | (bytes[ext_header_offset + 3] as u16);

        assert_eq!(ext_id, 0x1234);

        // Length in 32-bit words, so multiply by 4 to get bytes
        assert_eq!(ext_len_words * 4, 16); // 16 bytes (rounded up to multiple of 4)

        // Extension data starts after extension header
        let ext_data_offset = ext_header_offset + 4;
        let ext_data = &bytes[ext_data_offset..ext_data_offset + 14];

        // Check that the first bytes match our extension
        let expected_data = b"extension data";
        for i in 0..expected_data.len() {
            assert_eq!(ext_data[i], expected_data[i]);
        }
    }

    #[test]
    fn test_parse_rtp_packet_with_extension() {
        // Create a header with extension
        let mut header = RtpHeader::new(96, 1000, 12345, 0xABCDEF01);
        header.extension = true;

        // Create extensions with legacy format (0x1234 profile ID)
        let mut ext = RtpHeaderExtensions::new_legacy(0x1234);
        // Add a single extension element with the extension data
        ext.elements.push(ExtensionElement {
            id: 1, // Any ID for legacy format
            data: Bytes::from_static(b"extension data"),
        });
        header.extensions = Some(ext);

        // Create packet
        let payload = Bytes::from_static(b"test payload");
        let packet = RtpPacket::new(header, payload);

        // Serialize
        let bytes = packet.serialize().unwrap();

        // Parse back
        let parsed_packet = RtpPacket::parse(&bytes).unwrap();
        let parsed_header = parsed_packet.header;

        // Check extension fields
        assert_eq!(parsed_header.extension, true);
        assert!(parsed_header.extensions.is_some());

        let parsed_extensions = parsed_header.extensions.unwrap();
        assert_eq!(parsed_extensions.profile_id, 0x1234);
        assert!(!parsed_extensions.elements.is_empty());

        // Get the parsed extension data
        let parsed_data = &parsed_extensions.elements[0].data;

        // Compare the content, accounting for possible padding bytes
        let original_data = b"extension data";
        assert!(
            parsed_data.starts_with(original_data),
            "Extension data doesn't match original. Expected to start with: {:?}, got: {:?}",
            original_data,
            parsed_data
        );

        // Check that the payload is correctly parsed
        assert_eq!(parsed_packet.payload.as_ref(), b"test payload" as &[u8]);
    }
}