File
Blob: firmware/vendor/str0m/src/rtp/header.rs
| 1 | #![allow(clippy::unusual_byte_groupings)] |
| 2 | |
| 3 | use super::ext::{ExtensionMap, ExtensionValues, ExtensionsForm}; |
| 4 | use super::{MAX_BLANK_PADDING_PAYLOAD_SIZE, Pt, SeqNo, Ssrc}; |
| 5 | |
| 6 | /// Parsed header from an RTP packet. |
| 7 | #[derive(Debug, Clone, PartialEq, Eq)] |
| 8 | pub struct RtpHeader { |
| 9 | /// Always 2 |
| 10 | pub version: u8, |
| 11 | /// Whether the RTP packet has padding to be an equal of 4 bytes. |
| 12 | pub has_padding: bool, |
| 13 | /// RTP packet has "RTP header extensions". |
| 14 | pub has_extension: bool, |
| 15 | /// Number of contributing source identifiers. |
| 16 | pub csrc_count: usize, |
| 17 | /// For video, this marker signifies the end of a series of packets that |
| 18 | /// together form a single video frame. |
| 19 | /// For audio, it marks the beginning of a talkspurt, which is a burst of |
| 20 | /// audio packets. |
| 21 | pub marker: bool, |
| 22 | /// Type of payload being carried. What this correlates to is sent in the SDP. |
| 23 | pub payload_type: Pt, |
| 24 | /// Sequence number increasing by 1 for each RTP packet. |
| 25 | pub sequence_number: u16, |
| 26 | /// Timestamp in media time for the RTP packet. What the media time base is depends |
| 27 | /// on the codec. |
| 28 | pub timestamp: u32, |
| 29 | /// Sender source identifier. |
| 30 | pub ssrc: Ssrc, |
| 31 | /// Contributing source identifiers (CSRC). Up to 15 values per RFC 3550. |
| 32 | pub csrc: [u32; 15], |
| 33 | /// The extension values parsed using the mapping via SDP. |
| 34 | pub ext_vals: ExtensionValues, |
| 35 | /// Length of header. |
| 36 | pub header_len: usize, |
| 37 | } |
| 38 | |
| 39 | impl RtpHeader { |
| 40 | pub(crate) fn write_to(&self, buf: &mut [u8], exts: &ExtensionMap) -> usize { |
| 41 | assert!(self.csrc_count <= 15, "CSRC count must be <= 15"); |
| 42 | buf[0] = 0b10_0_0_0000 |
| 43 | | if self.has_padding { 1 << 5 } else { 0 } |
| 44 | | if self.has_extension { 1 << 4 } else { 0 } |
| 45 | | (self.csrc_count as u8); |
| 46 | |
| 47 | assert!(*self.payload_type <= 127); |
| 48 | buf[1] = *self.payload_type & 0b0111_1111 | if self.marker { 1 << 7 } else { 0 }; |
| 49 | |
| 50 | buf[2..4].copy_from_slice(&self.sequence_number.to_be_bytes()); |
| 51 | buf[4..8].copy_from_slice(&self.timestamp.to_be_bytes()); |
| 52 | buf[8..12].copy_from_slice(&self.ssrc.to_be_bytes()); |
| 53 | |
| 54 | let csrc_len = self.csrc_count * 4; |
| 55 | for i in 0..self.csrc_count { |
| 56 | let offset = 12 + i * 4; |
| 57 | buf[offset..offset + 4].copy_from_slice(&self.csrc[i].to_be_bytes()); |
| 58 | } |
| 59 | |
| 60 | let ext_start = 12 + csrc_len; |
| 61 | let exts_form = exts.form(&self.ext_vals); |
| 62 | buf[ext_start..ext_start + 2].copy_from_slice(&exts_form.serialize()); |
| 63 | |
| 64 | let ext_buf = &mut buf[ext_start + 4..]; |
| 65 | let mut ext_len = exts.write_to(ext_buf, &self.ext_vals, exts_form); |
| 66 | |
| 67 | let pad = 4 - ext_len % 4; |
| 68 | if pad < 4 { |
| 69 | ext_len += pad; |
| 70 | for i in 0..pad { |
| 71 | ext_buf[ext_len - i - 1] = 0; |
| 72 | } |
| 73 | } |
| 74 | |
| 75 | let bede_len = (ext_len / 4) as u16; |
| 76 | buf[ext_start + 2..ext_start + 4].copy_from_slice(&bede_len.to_be_bytes()); |
| 77 | |
| 78 | ext_start + 4 + ext_len |
| 79 | } |
| 80 | |
| 81 | fn do_pad(buf: &mut [u8], from: usize, pad: usize) { |
| 82 | assert!(pad <= 255); |
| 83 | |
| 84 | // set the padding bit |
| 85 | buf[0] |= 0b00_1_0_0000; |
| 86 | |
| 87 | for i in 0..pad { |
| 88 | buf[from + i] = 0; |
| 89 | } |
| 90 | |
| 91 | buf[from + pad - 1] = pad as u8; |
| 92 | } |
| 93 | |
| 94 | /// Write a packet consisting entirely of padding and write. |
| 95 | pub fn create_padding_packet( |
| 96 | buf: &mut [u8], |
| 97 | header_len: usize, |
| 98 | pad_len: u8, |
| 99 | block_size: usize, |
| 100 | ) -> usize { |
| 101 | if pad_len == 0 { |
| 102 | warn!("Not generating padding packet with zero length"); |
| 103 | return 0; |
| 104 | } |
| 105 | |
| 106 | let rounded_len = if pad_len as usize % block_size == 0 { |
| 107 | pad_len as usize |
| 108 | } else { |
| 109 | ((pad_len as usize / block_size) + 1) * block_size |
| 110 | } |
| 111 | .min(MAX_BLANK_PADDING_PAYLOAD_SIZE); |
| 112 | |
| 113 | Self::do_pad(buf, header_len, rounded_len); |
| 114 | |
| 115 | rounded_len |
| 116 | } |
| 117 | |
| 118 | /// Returns `buf` with any trailing SRTP padding removed, or `None` if the |
| 119 | /// trailing padding length is invalid. |
| 120 | pub(crate) fn unpad_payload(buf: &[u8]) -> Option<&[u8]> { |
| 121 | if buf.is_empty() { |
| 122 | return Some(buf); |
| 123 | } |
| 124 | let pad_len = buf[buf.len() - 1] as usize; |
| 125 | let unpadded_len = buf.len().checked_sub(pad_len)?; |
| 126 | Some(&buf[..unpadded_len]) |
| 127 | } |
| 128 | |
| 129 | pub(crate) fn parse(buf: &[u8], exts: &ExtensionMap) -> Option<RtpHeader> { |
| 130 | let orig_len = buf.len(); |
| 131 | if buf.len() < 12 { |
| 132 | trace!("RTP header too short < 12: {}", buf.len()); |
| 133 | return None; |
| 134 | } |
| 135 | |
| 136 | let version = (buf[0] & 0b1100_0000) >> 6; |
| 137 | if version != 2 { |
| 138 | trace!("RTP version is not 2"); |
| 139 | return None; |
| 140 | } |
| 141 | let has_padding = buf[0] & 0b0010_0000 > 0; |
| 142 | let has_extension = buf[0] & 0b0001_0000 > 0; |
| 143 | let csrc_count = (buf[0] & 0b0000_1111) as usize; |
| 144 | let marker = buf[1] & 0b1000_0000 > 0; |
| 145 | let payload_type = (buf[1] & 0b0111_1111).into(); |
| 146 | let sequence_number = u16::from_be_bytes([buf[2], buf[3]]); |
| 147 | |
| 148 | let timestamp = u32::from_be_bytes([buf[4], buf[5], buf[6], buf[7]]); |
| 149 | |
| 150 | let ssrc = u32::from_be_bytes([buf[8], buf[9], buf[10], buf[11]]); |
| 151 | |
| 152 | let buf: &[u8] = &buf[12..]; |
| 153 | |
| 154 | let csrc_len = 4 * csrc_count; |
| 155 | if buf.len() < csrc_len { |
| 156 | trace!("RTP header invalid, not enough csrc"); |
| 157 | return None; |
| 158 | } |
| 159 | |
| 160 | let mut csrc = [0_u32; 15]; |
| 161 | for (i, item) in csrc.iter_mut().enumerate().take(csrc_count) { |
| 162 | let offset = i * 4; |
| 163 | *item = u32::from_be_bytes([ |
| 164 | buf[offset], |
| 165 | buf[offset + 1], |
| 166 | buf[offset + 2], |
| 167 | buf[offset + 3], |
| 168 | ]); |
| 169 | } |
| 170 | |
| 171 | let buf: &[u8] = &buf[csrc_len..]; |
| 172 | |
| 173 | let mut ext = ExtensionValues { |
| 174 | ..Default::default() |
| 175 | }; |
| 176 | |
| 177 | let rest = if !has_extension { |
| 178 | buf |
| 179 | } else { |
| 180 | if buf.len() < 4 { |
| 181 | trace!("RTP bad header extension"); |
| 182 | return None; |
| 183 | } |
| 184 | |
| 185 | let Some(exts_form) = ExtensionsForm::parse([buf[0], buf[1]]) else { |
| 186 | trace!( |
| 187 | "Ignoring unknown RTP header extensions form: {:?}", |
| 188 | u16::from_be_bytes([buf[0], buf[1]]) |
| 189 | ); |
| 190 | return None; |
| 191 | }; |
| 192 | let ext_words = u16::from_be_bytes([buf[2], buf[3]]); |
| 193 | let ext_len = ext_words as usize * 4; |
| 194 | |
| 195 | let buf: &[u8] = &buf[4..]; |
| 196 | |
| 197 | if buf.len() < ext_len { |
| 198 | trace!("RTP ext len larger than header {} > {}", buf.len(), ext_len); |
| 199 | return None; |
| 200 | } |
| 201 | |
| 202 | exts.parse(&buf[..ext_len], exts_form, &mut ext); |
| 203 | &buf[ext_len..] |
| 204 | }; |
| 205 | |
| 206 | let header_len = orig_len - rest.len(); |
| 207 | |
| 208 | let ret = RtpHeader { |
| 209 | version, |
| 210 | has_padding, |
| 211 | has_extension, |
| 212 | csrc_count, |
| 213 | marker, |
| 214 | payload_type, |
| 215 | sequence_number, |
| 216 | timestamp, |
| 217 | ssrc: ssrc.into(), |
| 218 | csrc, |
| 219 | ext_vals: ext, |
| 220 | header_len, |
| 221 | }; |
| 222 | |
| 223 | Some(ret) |
| 224 | } |
| 225 | |
| 226 | /// For RTX the original sequence number is inserted before the RTP payload. |
| 227 | pub fn read_original_sequence_number(buf: &[u8], seq_no: &mut u16) -> usize { |
| 228 | if buf.len() < 2 { |
| 229 | // This is not an RTX packet, probably a padding packet |
| 230 | return 0; |
| 231 | } |
| 232 | *seq_no = u16::from_be_bytes([buf[0], buf[1]]); |
| 233 | 2 |
| 234 | } |
| 235 | |
| 236 | /// For RTX the original sequence number is inserted before the RTP payload. |
| 237 | pub fn write_original_sequence_number(buf: &mut [u8], seq_no: SeqNo) -> usize { |
| 238 | let seq_u16 = (*seq_no) as u16; |
| 239 | buf[0..2].copy_from_slice(&seq_u16.to_be_bytes()); |
| 240 | 2 |
| 241 | } |
| 242 | |
| 243 | /// Sequencer number of this RTP header given the previous number. |
| 244 | /// |
| 245 | /// The logic detects wrap-arounds of the 16-bit RTP sequence number. |
| 246 | pub fn sequence_number(&self, previous: Option<SeqNo>) -> SeqNo { |
| 247 | let e_seq = extend_u16(previous.map(|v| *v), self.sequence_number); |
| 248 | e_seq.into() |
| 249 | } |
| 250 | } |
| 251 | |
| 252 | macro_rules! mk_extend { |
| 253 | ($id:ident, $t:ty, $bits:expr) => { |
| 254 | /// "extend" a less than 64 bit sequence number into a 64 bit by |
| 255 | /// using the knowledge of the previous such sequence number. |
| 256 | pub fn $id(prev_ext_seq: Option<u64>, seq: $t) -> u64 { |
| 257 | const MAX: u64 = 2_u64.pow($bits); // u16: 65_536; |
| 258 | const HALF: u64 = MAX / 2; // u16: 32_768 |
| 259 | const BITS: usize = $bits; |
| 260 | const VAL_MASK: u64 = ((1 << BITS) - 1); |
| 261 | const ROC_MASK: i64 = (u64::MAX >> BITS) as i64; |
| 262 | |
| 263 | // We define the index of the SRTP packet corresponding to a given |
| 264 | // ROC and RTP sequence number to be the 48-bit quantity |
| 265 | // i = 2^16 * ROC + SEQ. |
| 266 | // |
| 267 | // https://tools.ietf.org/html/rfc3711#appendix-A |
| 268 | // |
| 269 | |
| 270 | // Mask seq to the N bits we are considering. |
| 271 | // As an example, this allows us to handle the wrap-around of 7 bit |
| 272 | // values while storing them in u8 |
| 273 | let seq = seq as u64 & VAL_MASK; |
| 274 | |
| 275 | if prev_ext_seq.is_none() { |
| 276 | // No wrap-around so far. |
| 277 | return seq; |
| 278 | } |
| 279 | |
| 280 | let prev_index = prev_ext_seq.unwrap(); |
| 281 | let roc = (prev_index >> BITS) as i64; // how many wrap-arounds. |
| 282 | let prev_seq = prev_index & (MAX - 1); // u16: 0xffff |
| 283 | |
| 284 | let v = if prev_seq < HALF { |
| 285 | if seq > HALF + prev_seq { |
| 286 | (roc - 1) & ROC_MASK |
| 287 | } else { |
| 288 | roc |
| 289 | } |
| 290 | } else if prev_seq > seq + HALF { |
| 291 | (roc + 1) & ROC_MASK |
| 292 | } else { |
| 293 | roc |
| 294 | }; |
| 295 | |
| 296 | if v < 0 { |
| 297 | return 0; |
| 298 | } |
| 299 | |
| 300 | (v as u64) * MAX + seq |
| 301 | } |
| 302 | }; |
| 303 | } |
| 304 | |
| 305 | mk_extend!(extend_u8, u8, 8); |
| 306 | mk_extend!(extend_u16, u16, 16); |
| 307 | mk_extend!(extend_u32, u32, 32); |
| 308 | |
| 309 | // we 'host' 7 bits in u8 but we ignore the most significant one |
| 310 | mk_extend!(extend_u7, u8, 7); |
| 311 | mk_extend!(extend_u15, u16, 15); |
| 312 | |
| 313 | impl Default for RtpHeader { |
| 314 | fn default() -> Self { |
| 315 | Self { |
| 316 | version: 2, |
| 317 | has_padding: false, |
| 318 | has_extension: true, |
| 319 | csrc_count: 0, |
| 320 | marker: false, |
| 321 | payload_type: 1.into(), |
| 322 | sequence_number: 0, |
| 323 | timestamp: 0, |
| 324 | ssrc: 0.into(), |
| 325 | csrc: [0; 15], |
| 326 | ext_vals: ExtensionValues::default(), |
| 327 | header_len: 16, |
| 328 | } |
| 329 | } |
| 330 | } |
| 331 | |
| 332 | #[cfg(test)] |
| 333 | mod test { |
| 334 | use crate::util::already_happened; |
| 335 | use crate::{io::DATAGRAM_MAX_PACKET_SIZE, rtp_::Extension}; |
| 336 | use std::time::Duration; |
| 337 | |
| 338 | use super::*; |
| 339 | |
| 340 | #[test] |
| 341 | fn extend_u16_wrap_around() { |
| 342 | assert_eq!(extend_u16(None, 0), 0); |
| 343 | assert_eq!(extend_u16(Some(0), 1), 1); |
| 344 | assert_eq!(extend_u16(Some(65_535), 0), 65_536); |
| 345 | assert_eq!(extend_u16(Some(65_500), 2), 65_538); |
| 346 | assert_eq!(extend_u16(Some(2), 1), 1); |
| 347 | assert_eq!(extend_u16(Some(65_538), 1), 65_537); |
| 348 | assert_eq!(extend_u16(Some(3), 3), 3); |
| 349 | assert_eq!(extend_u16(Some(65_500), 65_500), 65_500); |
| 350 | } |
| 351 | |
| 352 | #[test] |
| 353 | fn extend_u16_with_0_prev() { |
| 354 | // This tests going backwards from previous 0. This should wrap |
| 355 | // around "backwards" making a ridiculous number. |
| 356 | let seq = u16::MAX / 2 + 2; |
| 357 | let expected = u64::MAX - (u16::MAX - seq) as u64; |
| 358 | assert_eq!(extend_u16(Some(0), seq), expected); |
| 359 | } |
| 360 | |
| 361 | #[test] |
| 362 | fn extend_u32_wrap_around() { |
| 363 | const U32MAX: u64 = u32::MAX as u64 + 1; |
| 364 | assert_eq!(extend_u32(None, 0), 0); |
| 365 | assert_eq!(extend_u32(Some(0), 1), 1); |
| 366 | assert_eq!(extend_u32(Some(U32MAX - 1), 0), U32MAX); |
| 367 | assert_eq!(extend_u32(Some(U32MAX - 32), 2), U32MAX + 2); |
| 368 | assert_eq!(extend_u32(Some(2), 1), 1); |
| 369 | assert_eq!(extend_u32(Some(U32MAX + 2), 1), U32MAX + 1); |
| 370 | assert_eq!(extend_u32(Some(3), 3), 3); |
| 371 | assert_eq!( |
| 372 | extend_u32(Some(U32MAX - 32), (U32MAX - 32) as u32), |
| 373 | U32MAX - 32 |
| 374 | ); |
| 375 | } |
| 376 | |
| 377 | #[test] |
| 378 | fn extend_u32_with_0_prev() { |
| 379 | // This tests going backwards from previous 0. This should wrap |
| 380 | // around "backwards" making a ridiculous number. |
| 381 | let seq = u32::MAX / 2 + 2; |
| 382 | let expected = u64::MAX - (u32::MAX - seq) as u64; |
| 383 | assert_eq!(extend_u32(Some(0), seq), expected); |
| 384 | } |
| 385 | |
| 386 | #[test] |
| 387 | fn extend_u7_wrap_around() { |
| 388 | let max = 2_u64.pow(7) - 1; |
| 389 | let half = max / 2; |
| 390 | assert_eq!(extend_u7(None, 0), 0); |
| 391 | assert_eq!(extend_u7(None, 1), 1); |
| 392 | |
| 393 | // most significant bit gets ignored |
| 394 | assert_eq!(extend_u7(None, max as u8), max); |
| 395 | assert_eq!(extend_u7(None, (max + 1) as u8), 0); |
| 396 | |
| 397 | assert_eq!(extend_u7(Some(0), half as u8), half); |
| 398 | assert_eq!(extend_u7(Some(0), (half + 1) as u8), half + 1); |
| 399 | |
| 400 | let seq = max / 2 + 2; |
| 401 | let expected = u64::MAX - (max - seq); |
| 402 | assert_eq!(extend_u7(Some(0), seq as u8), expected); |
| 403 | } |
| 404 | |
| 405 | #[test] |
| 406 | fn extend_u15_wrap_around() { |
| 407 | let max = 2_u64.pow(15) - 1; |
| 408 | let half = max / 2; |
| 409 | assert_eq!(extend_u15(None, 0), 0); |
| 410 | assert_eq!(extend_u15(None, 1), 1); |
| 411 | |
| 412 | assert_eq!(extend_u15(None, max as u16), max); |
| 413 | assert_eq!(extend_u15(None, (max + 1) as u16), 0); |
| 414 | |
| 415 | assert_eq!(extend_u15(Some(0), half as u16), half); |
| 416 | assert_eq!(extend_u15(Some(0), (half + 1) as u16), half + 1); |
| 417 | |
| 418 | let seq = max / 2 + 2; |
| 419 | let expected = u64::MAX - (max - seq); |
| 420 | assert_eq!(extend_u15(Some(0), seq as u16), expected); |
| 421 | } |
| 422 | |
| 423 | #[test] |
| 424 | fn test_generate_one_length_padding_packet() { |
| 425 | let mut buf = vec![6; 255]; |
| 426 | RtpHeader::create_padding_packet(&mut buf, 10, 1, 16); |
| 427 | |
| 428 | let mut expected = vec![0; 16]; |
| 429 | expected[15] = 16; |
| 430 | assert_eq!(&buf[10..26], &expected); |
| 431 | } |
| 432 | |
| 433 | // version: 2, |
| 434 | // has_padding: false, |
| 435 | // has_extension: true, |
| 436 | // marker: false, |
| 437 | // payload_type: 1.into(), |
| 438 | // sequence_number: 0, |
| 439 | // timestamp: 0, |
| 440 | // ssrc: 0.into(), |
| 441 | // ext_vals: ExtensionValues::default(), |
| 442 | // header_len: 16, |
| 443 | |
| 444 | #[test] |
| 445 | fn test_write_rtp_headers() { |
| 446 | fn mk_header(seq: u16, ts: u32, level: i8, marker: bool, exts: &ExtensionMap) -> Vec<u8> { |
| 447 | let header = RtpHeader { |
| 448 | payload_type: 33.into(), |
| 449 | sequence_number: seq, |
| 450 | timestamp: ts, |
| 451 | ssrc: 44.into(), |
| 452 | marker, |
| 453 | ext_vals: ExtensionValues { |
| 454 | audio_level: Some(level), |
| 455 | voice_activity: Some(false), |
| 456 | ..Default::default() |
| 457 | }, |
| 458 | ..Default::default() |
| 459 | }; |
| 460 | let mut buf = vec![0; DATAGRAM_MAX_PACKET_SIZE]; |
| 461 | let n = header.write_to(&mut buf[..], exts); |
| 462 | buf.truncate(n); |
| 463 | |
| 464 | buf |
| 465 | } |
| 466 | |
| 467 | let mut exts = ExtensionMap::empty(); |
| 468 | exts.set(3, Extension::AudioLevel); |
| 469 | |
| 470 | let buf1 = mk_header(47_000, 10_000, -42, false, &exts); |
| 471 | let buf2 = mk_header(47_001, 12_000, -43, true, &exts); |
| 472 | let buf3 = mk_header(47_002, 14_000, -44, false, &exts); |
| 473 | |
| 474 | let p1 = &[ |
| 475 | 144, 33, 183, 152, 0, 0, 39, 16, 0, 0, 0, 44, 0xBE, 0xDE, 0, 1, 48, 170, 0, 0, |
| 476 | ]; |
| 477 | let p2 = &[ |
| 478 | 144, 161, 183, 153, 0, 0, 46, 224, 0, 0, 0, 44, 0xBE, 0xDE, 0, 1, 48, 171, 0, 0, |
| 479 | ]; |
| 480 | let p3 = &[ |
| 481 | 144, 33, 183, 154, 0, 0, 54, 176, 0, 0, 0, 44, 0xBE, 0xDE, 0, 1, 48, 172, 0, 0, |
| 482 | ]; |
| 483 | |
| 484 | assert_eq!(&buf1, p1); |
| 485 | assert_eq!(&buf2, p2); |
| 486 | assert_eq!(&buf3, p3); |
| 487 | } |
| 488 | |
| 489 | #[test] |
| 490 | fn test_write_rtp_headers_two_byte_form() { |
| 491 | fn mk_header(seq: u16, ts: u32, level: i8, marker: bool, exts: &ExtensionMap) -> Vec<u8> { |
| 492 | let header = RtpHeader { |
| 493 | payload_type: 33.into(), |
| 494 | sequence_number: seq, |
| 495 | timestamp: ts, |
| 496 | ssrc: 44.into(), |
| 497 | marker, |
| 498 | ext_vals: ExtensionValues { |
| 499 | audio_level: Some(level), |
| 500 | voice_activity: Some(false), |
| 501 | ..Default::default() |
| 502 | }, |
| 503 | ..Default::default() |
| 504 | }; |
| 505 | let mut buf = vec![0; DATAGRAM_MAX_PACKET_SIZE]; |
| 506 | let n = header.write_to(&mut buf[..], exts); |
| 507 | buf.truncate(n); |
| 508 | |
| 509 | buf |
| 510 | } |
| 511 | |
| 512 | let mut exts = ExtensionMap::empty(); |
| 513 | // An ID larger than 14 forces the 2-byte header extension form |
| 514 | exts.set(15, Extension::AudioLevel); |
| 515 | |
| 516 | let buf1 = mk_header(47_000, 10_000, -42, false, &exts); |
| 517 | let buf2 = mk_header(47_001, 12_000, -43, true, &exts); |
| 518 | let buf3 = mk_header(47_002, 14_000, -44, false, &exts); |
| 519 | |
| 520 | let p1 = &[ |
| 521 | 144, 33, 183, 152, 0, 0, 39, 16, 0, 0, 0, 44, 0x10, 0x00, 0, 1, 15, 1, 170, 0, |
| 522 | ]; |
| 523 | let p2 = &[ |
| 524 | 144, 161, 183, 153, 0, 0, 46, 224, 0, 0, 0, 44, 0x10, 0x00, 0, 1, 15, 1, 171, 0, |
| 525 | ]; |
| 526 | let p3 = &[ |
| 527 | 144, 33, 183, 154, 0, 0, 54, 176, 0, 0, 0, 44, 0x10, 0x00, 0, 1, 15, 1, 172, 0, |
| 528 | ]; |
| 529 | |
| 530 | assert_eq!(&buf1, p1); |
| 531 | assert_eq!(&buf2, p2); |
| 532 | assert_eq!(&buf3, p3); |
| 533 | } |
| 534 | |
| 535 | #[test] |
| 536 | fn test_parse_rtp_headers() { |
| 537 | let exts = ExtensionMap::standard(); |
| 538 | |
| 539 | let hb1 = [ |
| 540 | 176, 111, 183, 152, 0, 0, 39, 16, 46, 87, 21, 249, 0xBE, 0xDE, 0, 4, 16, 170, 34, 254, |
| 541 | 32, 106, 49, 0, 0, 66, 120, 89, 106, 0, 0, 0, 1, 2, 3, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| 542 | 0, 12, |
| 543 | ]; |
| 544 | |
| 545 | let hb2 = [ |
| 546 | 176, 111, 183, 153, 0, 0, 46, 224, 46, 87, 21, 249, 0xBE, 0xDE, 0, 4, 16, 171, 34, 254, |
| 547 | 134, 208, 49, 0, 2, 66, 120, 89, 106, 0, 0, 0, 5, 6, 7, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| 548 | 0, 0, 12, |
| 549 | ]; |
| 550 | let hb3 = [ |
| 551 | 176, 111, 183, 154, 0, 0, 54, 176, 46, 87, 21, 249, 0xBE, 0xDE, 0, 4, 16, 172, 34, 254, |
| 552 | 32, 106, 49, 0, 1, 66, 120, 89, 106, 0, 0, 0, 9, 10, 11, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| 553 | 0, 0, 12, |
| 554 | ]; |
| 555 | |
| 556 | let h1 = RtpHeader::parse(&hb1, &exts).unwrap(); |
| 557 | let abs1 = already_happened() + Duration::from_micros(63531654); |
| 558 | |
| 559 | assert_eq!( |
| 560 | h1, |
| 561 | RtpHeader { |
| 562 | version: 2, |
| 563 | has_padding: true, |
| 564 | has_extension: true, |
| 565 | csrc_count: 0, |
| 566 | marker: false, |
| 567 | payload_type: 111.into(), |
| 568 | sequence_number: 47000, |
| 569 | timestamp: 10000, |
| 570 | ssrc: 777459193.into(), |
| 571 | csrc: [0; 15], |
| 572 | ext_vals: ExtensionValues { |
| 573 | mid: Some("xYj".into()), |
| 574 | abs_send_time: Some(abs1), |
| 575 | voice_activity: Some(true), |
| 576 | audio_level: Some(-42), |
| 577 | transport_cc: Some(0), |
| 578 | ..Default::default() |
| 579 | }, |
| 580 | header_len: 32 |
| 581 | } |
| 582 | ); |
| 583 | |
| 584 | let h2 = RtpHeader::parse(&hb2, &exts).unwrap(); |
| 585 | let abs2 = already_happened() + Duration::from_micros(63631652); |
| 586 | |
| 587 | assert_eq!( |
| 588 | h2, |
| 589 | RtpHeader { |
| 590 | version: 2, |
| 591 | has_padding: true, |
| 592 | has_extension: true, |
| 593 | csrc_count: 0, |
| 594 | marker: false, |
| 595 | payload_type: 111.into(), |
| 596 | sequence_number: 47001, |
| 597 | timestamp: 12000, |
| 598 | ssrc: 777459193.into(), |
| 599 | csrc: [0; 15], |
| 600 | ext_vals: ExtensionValues { |
| 601 | mid: Some("xYj".into()), |
| 602 | abs_send_time: Some(abs2), |
| 603 | voice_activity: Some(true), |
| 604 | audio_level: Some(-43), |
| 605 | transport_cc: Some(2), |
| 606 | ..Default::default() |
| 607 | }, |
| 608 | header_len: 32 |
| 609 | } |
| 610 | ); |
| 611 | |
| 612 | let h3 = RtpHeader::parse(&hb3, &exts).unwrap(); |
| 613 | let abs3 = already_happened() + Duration::from_micros(63531654); |
| 614 | |
| 615 | assert_eq!( |
| 616 | h3, |
| 617 | RtpHeader { |
| 618 | version: 2, |
| 619 | has_padding: true, |
| 620 | has_extension: true, |
| 621 | csrc_count: 0, |
| 622 | marker: false, |
| 623 | payload_type: 111.into(), |
| 624 | sequence_number: 47002, |
| 625 | timestamp: 14000, |
| 626 | ssrc: 777459193.into(), |
| 627 | csrc: [0; 15], |
| 628 | ext_vals: ExtensionValues { |
| 629 | mid: Some("xYj".into()), |
| 630 | abs_send_time: Some(abs3), |
| 631 | voice_activity: Some(true), |
| 632 | audio_level: Some(-44), |
| 633 | transport_cc: Some(1), |
| 634 | ..Default::default() |
| 635 | }, |
| 636 | header_len: 32 |
| 637 | } |
| 638 | ); |
| 639 | } |
| 640 | |
| 641 | #[test] |
| 642 | fn test_parse_rtp_headers_two_byte_form() { |
| 643 | let exts = ExtensionMap::standard(); |
| 644 | |
| 645 | let hb1 = [ |
| 646 | 176, 111, 183, 152, 0, 0, 39, 16, 46, 87, 21, 249, 0x10, 0x01, 0, 5, 1, 1, 170, 2, 3, |
| 647 | 254, 32, 106, 3, 2, 0, 0, 4, 3, 120, 89, 106, 0, 0, 0, 1, 2, 3, 4, 0, 0, 0, 0, 0, 0, 0, |
| 648 | 0, 0, 0, 0, 12, |
| 649 | ]; |
| 650 | |
| 651 | let hb2 = [ |
| 652 | 176, 111, 183, 153, 0, 0, 46, 224, 46, 87, 21, 249, 0x10, 0x02, 0, 5, 1, 1, 171, 2, 3, |
| 653 | 254, 134, 208, 3, 2, 0, 2, 4, 3, 120, 89, 106, 0, 0, 0, 5, 6, 7, 8, 0, 0, 0, 0, 0, 0, |
| 654 | 0, 0, 0, 0, 0, 12, |
| 655 | ]; |
| 656 | let hb3 = [ |
| 657 | 176, 111, 183, 154, 0, 0, 54, 176, 46, 87, 21, 249, 0x10, 0x03, 0, 5, 1, 1, 172, 2, 3, |
| 658 | 254, 32, 106, 3, 2, 0, 1, 4, 3, 120, 89, 106, 0, 0, 0, 9, 10, 11, 12, 0, 0, 0, 0, 0, 0, |
| 659 | 0, 0, 0, 0, 0, 12, |
| 660 | ]; |
| 661 | |
| 662 | let h1 = RtpHeader::parse(&hb1, &exts).unwrap(); |
| 663 | let abs1 = already_happened() + Duration::from_micros(63531654); |
| 664 | |
| 665 | assert_eq!( |
| 666 | h1, |
| 667 | RtpHeader { |
| 668 | version: 2, |
| 669 | has_padding: true, |
| 670 | has_extension: true, |
| 671 | csrc_count: 0, |
| 672 | marker: false, |
| 673 | payload_type: 111.into(), |
| 674 | sequence_number: 47000, |
| 675 | timestamp: 10000, |
| 676 | ssrc: 777459193.into(), |
| 677 | csrc: [0; 15], |
| 678 | ext_vals: ExtensionValues { |
| 679 | mid: Some("xYj".into()), |
| 680 | abs_send_time: Some(abs1), |
| 681 | voice_activity: Some(true), |
| 682 | audio_level: Some(-42), |
| 683 | transport_cc: Some(0), |
| 684 | ..Default::default() |
| 685 | }, |
| 686 | header_len: 36 |
| 687 | } |
| 688 | ); |
| 689 | |
| 690 | let h2 = RtpHeader::parse(&hb2, &exts).unwrap(); |
| 691 | let abs2 = already_happened() + Duration::from_micros(63631652); |
| 692 | |
| 693 | assert_eq!( |
| 694 | h2, |
| 695 | RtpHeader { |
| 696 | version: 2, |
| 697 | has_padding: true, |
| 698 | has_extension: true, |
| 699 | csrc_count: 0, |
| 700 | marker: false, |
| 701 | payload_type: 111.into(), |
| 702 | sequence_number: 47001, |
| 703 | timestamp: 12000, |
| 704 | ssrc: 777459193.into(), |
| 705 | csrc: [0; 15], |
| 706 | ext_vals: ExtensionValues { |
| 707 | mid: Some("xYj".into()), |
| 708 | abs_send_time: Some(abs2), |
| 709 | voice_activity: Some(true), |
| 710 | audio_level: Some(-43), |
| 711 | transport_cc: Some(2), |
| 712 | ..Default::default() |
| 713 | }, |
| 714 | header_len: 36 |
| 715 | } |
| 716 | ); |
| 717 | |
| 718 | let h3 = RtpHeader::parse(&hb3, &exts).unwrap(); |
| 719 | let abs3 = already_happened() + Duration::from_micros(63531654); |
| 720 | |
| 721 | assert_eq!( |
| 722 | h3, |
| 723 | RtpHeader { |
| 724 | version: 2, |
| 725 | has_padding: true, |
| 726 | has_extension: true, |
| 727 | csrc_count: 0, |
| 728 | marker: false, |
| 729 | payload_type: 111.into(), |
| 730 | sequence_number: 47002, |
| 731 | timestamp: 14000, |
| 732 | ssrc: 777459193.into(), |
| 733 | csrc: [0; 15], |
| 734 | ext_vals: ExtensionValues { |
| 735 | mid: Some("xYj".into()), |
| 736 | abs_send_time: Some(abs3), |
| 737 | voice_activity: Some(true), |
| 738 | audio_level: Some(-44), |
| 739 | transport_cc: Some(1), |
| 740 | ..Default::default() |
| 741 | }, |
| 742 | header_len: 36 |
| 743 | } |
| 744 | ); |
| 745 | } |
| 746 | |
| 747 | #[test] |
| 748 | fn test_write_rtp_headers_with_csrc() { |
| 749 | fn mk_header(csrc_count: usize, csrc: [u32; 15], exts: &ExtensionMap) -> Vec<u8> { |
| 750 | let header = RtpHeader { |
| 751 | csrc_count, |
| 752 | payload_type: 33.into(), |
| 753 | sequence_number: 47_000, |
| 754 | timestamp: 10_000, |
| 755 | ssrc: 44.into(), |
| 756 | csrc, |
| 757 | ext_vals: ExtensionValues { |
| 758 | audio_level: Some(-42), |
| 759 | voice_activity: Some(true), |
| 760 | ..Default::default() |
| 761 | }, |
| 762 | ..Default::default() |
| 763 | }; |
| 764 | let mut buf = vec![0; DATAGRAM_MAX_PACKET_SIZE]; |
| 765 | let n = header.write_to(&mut buf[..], exts); |
| 766 | buf.truncate(n); |
| 767 | buf |
| 768 | } |
| 769 | |
| 770 | let mut exts = ExtensionMap::empty(); |
| 771 | exts.set(3, Extension::AudioLevel); |
| 772 | |
| 773 | // 3 CSRC entries, one-byte extension form |
| 774 | let buf1 = mk_header( |
| 775 | 3, |
| 776 | [ |
| 777 | 100, |
| 778 | 0x12_34_56_78, |
| 779 | 0xFFFF_FFFF, |
| 780 | 0, |
| 781 | 0, |
| 782 | 0, |
| 783 | 0, |
| 784 | 0, |
| 785 | 0, |
| 786 | 0, |
| 787 | 0, |
| 788 | 0, |
| 789 | 0, |
| 790 | 0, |
| 791 | 0, |
| 792 | ], |
| 793 | &exts, |
| 794 | ); |
| 795 | let p1 = &[ |
| 796 | 147, 33, 183, 152, 0, 0, 39, 16, 0, 0, 0, 44, // fixed header (CC=3) |
| 797 | 0, 0, 0, 100, 0x12, 0x34, 0x56, 0x78, 0xFF, 0xFF, 0xFF, 0xFF, // CSRC values |
| 798 | 0xBE, 0xDE, 0, 1, 48, 170, 0, 0, // extension |
| 799 | ]; |
| 800 | assert_eq!(&buf1, p1); |
| 801 | |
| 802 | // 15 CSRC entries (maximum) |
| 803 | let buf2 = mk_header( |
| 804 | 15, |
| 805 | [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], |
| 806 | &exts, |
| 807 | ); |
| 808 | let p2 = &[ |
| 809 | 159, 33, 183, 152, 0, 0, 39, 16, 0, 0, 0, 44, // fixed header (CC=15) |
| 810 | 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0, 4, // CSRC 1-4 |
| 811 | 0, 0, 0, 5, 0, 0, 0, 6, 0, 0, 0, 7, 0, 0, 0, 8, // CSRC 5-8 |
| 812 | 0, 0, 0, 9, 0, 0, 0, 10, 0, 0, 0, 11, 0, 0, 0, 12, // CSRC 9-12 |
| 813 | 0, 0, 0, 13, 0, 0, 0, 14, 0, 0, 0, 15, // CSRC 13-15 |
| 814 | 0xBE, 0xDE, 0, 1, 48, 170, 0, 0, // extension |
| 815 | ]; |
| 816 | assert_eq!(&buf2, p2); |
| 817 | } |
| 818 | |
| 819 | #[test] |
| 820 | fn test_parse_rtp_headers_with_csrc() { |
| 821 | let mut exts = ExtensionMap::empty(); |
| 822 | exts.set(3, Extension::AudioLevel); |
| 823 | |
| 824 | // 3 CSRC entries with one-byte extension form (produced by test_write_rtp_headers_with_csrc) |
| 825 | let buf1 = [ |
| 826 | 147, 33, 183, 152, 0, 0, 39, 16, 0, 0, 0, 44, 0, 0, 0, 100, 0x12, 0x34, 0x56, 0x78, |
| 827 | 0xFF, 0xFF, 0xFF, 0xFF, 0xBE, 0xDE, 0, 1, 48, 170, 0, 0, |
| 828 | ]; |
| 829 | |
| 830 | let h1 = RtpHeader::parse(&buf1, &exts).unwrap(); |
| 831 | |
| 832 | assert_eq!( |
| 833 | h1, |
| 834 | RtpHeader { |
| 835 | version: 2, |
| 836 | has_padding: false, |
| 837 | has_extension: true, |
| 838 | csrc_count: 3, |
| 839 | marker: false, |
| 840 | payload_type: 33.into(), |
| 841 | sequence_number: 47_000, |
| 842 | timestamp: 10_000, |
| 843 | ssrc: 44.into(), |
| 844 | csrc: [ |
| 845 | 100, |
| 846 | 0x12_34_56_78, |
| 847 | 0xFFFF_FFFF, |
| 848 | 0, |
| 849 | 0, |
| 850 | 0, |
| 851 | 0, |
| 852 | 0, |
| 853 | 0, |
| 854 | 0, |
| 855 | 0, |
| 856 | 0, |
| 857 | 0, |
| 858 | 0, |
| 859 | 0 |
| 860 | ], |
| 861 | ext_vals: ExtensionValues { |
| 862 | audio_level: Some(-42), |
| 863 | voice_activity: Some(true), |
| 864 | ..Default::default() |
| 865 | }, |
| 866 | header_len: 32, |
| 867 | } |
| 868 | ); |
| 869 | |
| 870 | // 15 CSRC entries (maximum) |
| 871 | let buf2 = [ |
| 872 | 159, 33, 183, 152, 0, 0, 39, 16, 0, 0, 0, 44, // fixed header (CC=15) |
| 873 | 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0, 4, // CSRC 1-4 |
| 874 | 0, 0, 0, 5, 0, 0, 0, 6, 0, 0, 0, 7, 0, 0, 0, 8, // CSRC 5-8 |
| 875 | 0, 0, 0, 9, 0, 0, 0, 10, 0, 0, 0, 11, 0, 0, 0, 12, // CSRC 9-12 |
| 876 | 0, 0, 0, 13, 0, 0, 0, 14, 0, 0, 0, 15, // CSRC 13-15 |
| 877 | 0xBE, 0xDE, 0, 1, 48, 170, 0, 0, // extension |
| 878 | ]; |
| 879 | |
| 880 | let h2 = RtpHeader::parse(&buf2, &exts).unwrap(); |
| 881 | |
| 882 | assert_eq!( |
| 883 | h2, |
| 884 | RtpHeader { |
| 885 | version: 2, |
| 886 | has_padding: false, |
| 887 | has_extension: true, |
| 888 | csrc_count: 15, |
| 889 | marker: false, |
| 890 | payload_type: 33.into(), |
| 891 | sequence_number: 47_000, |
| 892 | timestamp: 10_000, |
| 893 | ssrc: 44.into(), |
| 894 | csrc: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], |
| 895 | ext_vals: ExtensionValues { |
| 896 | audio_level: Some(-42), |
| 897 | voice_activity: Some(true), |
| 898 | ..Default::default() |
| 899 | }, |
| 900 | header_len: 80, |
| 901 | } |
| 902 | ); |
| 903 | } |
| 904 | |
| 905 | #[test] |
| 906 | fn truncate_off_srtp_padding() { |
| 907 | let truncate = |payload: Vec<u8>| -> Result<Vec<u8>, ()> { |
| 908 | match RtpHeader::unpad_payload(&payload) { |
| 909 | Some(unpadded) => Ok(unpadded.to_vec()), |
| 910 | None => Err(()), |
| 911 | } |
| 912 | }; |
| 913 | |
| 914 | assert_eq!(Ok(vec![1, 2, 3, 4, 0]), truncate(vec![1, 2, 3, 4, 0])); |
| 915 | assert_eq!(Ok(vec![1, 2, 3, 4]), truncate(vec![1, 2, 3, 4, 1])); |
| 916 | assert_eq!(Ok(vec![1, 2, 3]), truncate(vec![1, 2, 3, 4, 2])); |
| 917 | assert_eq!(Ok(vec![1, 2]), truncate(vec![1, 2, 3, 4, 3])); |
| 918 | assert_eq!(Ok(vec![1]), truncate(vec![1, 2, 3, 4, 4])); |
| 919 | assert_eq!(Ok(vec![]), truncate(vec![1, 2, 3, 4, 5])); |
| 920 | assert_eq!(Err(()), truncate(vec![1, 2, 3, 4, 6])); |
| 921 | assert_eq!(Err(()), truncate(vec![1, 2, 3, 4, 255])); |
| 922 | assert_eq!(Ok(vec![0]), truncate(vec![0])); |
| 923 | assert_eq!(Ok(vec![]), truncate(vec![1])); |
| 924 | assert_eq!(Ok(vec![]), truncate(vec![])); |
| 925 | } |
| 926 | } |