#![allow(clippy::unusual_byte_groupings)]
use super::ext::{ExtensionMap, ExtensionValues, ExtensionsForm};
use super::{MAX_BLANK_PADDING_PAYLOAD_SIZE, Pt, SeqNo, Ssrc};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct RtpHeader {
pub version: u8,
pub has_padding: bool,
pub has_extension: bool,
pub csrc_count: usize,
pub marker: bool,
pub payload_type: Pt,
pub sequence_number: u16,
pub timestamp: u32,
pub ssrc: Ssrc,
pub csrc: [u32; 15],
pub ext_vals: ExtensionValues,
pub header_len: usize,
}
impl RtpHeader {
pub(crate) fn write_to(&self, buf: &mut [u8], exts: &ExtensionMap) -> usize {
assert!(self.csrc_count <= 15, "CSRC count must be <= 15");
buf[0] = 0b10_0_0_0000
| if self.has_padding { 1 << 5 } else { 0 }
| if self.has_extension { 1 << 4 } else { 0 }
| (self.csrc_count as u8);
assert!(*self.payload_type <= 127);
buf[1] = *self.payload_type & 0b0111_1111 | if self.marker { 1 << 7 } else { 0 };
buf[2..4].copy_from_slice(&self.sequence_number.to_be_bytes());
buf[4..8].copy_from_slice(&self.timestamp.to_be_bytes());
buf[8..12].copy_from_slice(&self.ssrc.to_be_bytes());
let csrc_len = self.csrc_count * 4;
for i in 0..self.csrc_count {
let offset = 12 + i * 4;
buf[offset..offset + 4].copy_from_slice(&self.csrc[i].to_be_bytes());
}
let ext_start = 12 + csrc_len;
let exts_form = exts.form(&self.ext_vals);
buf[ext_start..ext_start + 2].copy_from_slice(&exts_form.serialize());
let ext_buf = &mut buf[ext_start + 4..];
let mut ext_len = exts.write_to(ext_buf, &self.ext_vals, exts_form);
let pad = 4 - ext_len % 4;
if pad < 4 {
ext_len += pad;
for i in 0..pad {
ext_buf[ext_len - i - 1] = 0;
}
}
let bede_len = (ext_len / 4) as u16;
buf[ext_start + 2..ext_start + 4].copy_from_slice(&bede_len.to_be_bytes());
ext_start + 4 + ext_len
}
fn do_pad(buf: &mut [u8], from: usize, pad: usize) {
assert!(pad <= 255);
buf[0] |= 0b00_1_0_0000;
for i in 0..pad {
buf[from + i] = 0;
}
buf[from + pad - 1] = pad as u8;
}
pub(crate) fn pad_packet(
buf: &mut [u8],
header_len: usize,
body_len: usize,
block_size: usize,
) -> usize {
let pad_len = block_size - body_len % block_size;
if pad_len == block_size {
return 0;
}
Self::do_pad(buf, header_len + body_len, pad_len);
pad_len
}
pub fn create_padding_packet(
buf: &mut [u8],
header_len: usize,
pad_len: u8,
block_size: usize,
) -> usize {
if pad_len == 0 {
warn!("Not generating padding packet with zero length");
return 0;
}
let rounded_len = if pad_len as usize % block_size == 0 {
pad_len as usize
} else {
((pad_len as usize / block_size) + 1) * block_size
}
.min(MAX_BLANK_PADDING_PAYLOAD_SIZE);
Self::do_pad(buf, header_len, rounded_len);
rounded_len
}
pub(crate) fn unpad_payload(buf: &mut Vec<u8>) -> bool {
if buf.is_empty() {
return true;
}
let pad_len = buf[buf.len() - 1] as usize;
let Some(unpadded_len) = buf.len().checked_sub(pad_len) else {
return false;
};
buf.truncate(unpadded_len);
true
}
pub(crate) fn parse(buf: &[u8], exts: &ExtensionMap) -> Option<RtpHeader> {
let orig_len = buf.len();
if buf.len() < 12 {
trace!("RTP header too short < 12: {}", buf.len());
return None;
}
let version = (buf[0] & 0b1100_0000) >> 6;
if version != 2 {
trace!("RTP version is not 2");
return None;
}
let has_padding = buf[0] & 0b0010_0000 > 0;
let has_extension = buf[0] & 0b0001_0000 > 0;
let csrc_count = (buf[0] & 0b0000_1111) as usize;
let marker = buf[1] & 0b1000_0000 > 0;
let payload_type = (buf[1] & 0b0111_1111).into();
let sequence_number = u16::from_be_bytes([buf[2], buf[3]]);
let timestamp = u32::from_be_bytes([buf[4], buf[5], buf[6], buf[7]]);
let ssrc = u32::from_be_bytes([buf[8], buf[9], buf[10], buf[11]]);
let buf: &[u8] = &buf[12..];
let csrc_len = 4 * csrc_count;
if buf.len() < csrc_len {
trace!("RTP header invalid, not enough csrc");
return None;
}
let mut csrc = [0_u32; 15];
for (i, item) in csrc.iter_mut().enumerate().take(csrc_count) {
let offset = i * 4;
*item = u32::from_be_bytes([
buf[offset],
buf[offset + 1],
buf[offset + 2],
buf[offset + 3],
]);
}
let buf: &[u8] = &buf[csrc_len..];
let mut ext = ExtensionValues {
..Default::default()
};
let rest = if !has_extension {
buf
} else {
if buf.len() < 4 {
trace!("RTP bad header extension");
return None;
}
let Some(exts_form) = ExtensionsForm::parse([buf[0], buf[1]]) else {
trace!(
"Ignoring unknown RTP header extensions form: {:?}",
u16::from_be_bytes([buf[0], buf[1]])
);
return None;
};
let ext_words = u16::from_be_bytes([buf[2], buf[3]]);
let ext_len = ext_words as usize * 4;
let buf: &[u8] = &buf[4..];
if buf.len() < ext_len {
trace!("RTP ext len larger than header {} > {}", buf.len(), ext_len);
return None;
}
exts.parse(&buf[..ext_len], exts_form, &mut ext);
&buf[ext_len..]
};
let header_len = orig_len - rest.len();
let ret = RtpHeader {
version,
has_padding,
has_extension,
csrc_count,
marker,
payload_type,
sequence_number,
timestamp,
ssrc: ssrc.into(),
csrc,
ext_vals: ext,
header_len,
};
Some(ret)
}
pub fn read_original_sequence_number(buf: &[u8], seq_no: &mut u16) -> usize {
if buf.len() < 2 {
return 0;
}
*seq_no = u16::from_be_bytes([buf[0], buf[1]]);
2
}
pub fn write_original_sequence_number(buf: &mut [u8], seq_no: SeqNo) -> usize {
let seq_u16 = (*seq_no) as u16;
buf[0..2].copy_from_slice(&seq_u16.to_be_bytes());
2
}
pub fn sequence_number(&self, previous: Option<SeqNo>) -> SeqNo {
let e_seq = extend_u16(previous.map(|v| *v), self.sequence_number);
e_seq.into()
}
}
macro_rules! mk_extend {
($id:ident, $t:ty, $bits:expr) => {
pub fn $id(prev_ext_seq: Option<u64>, seq: $t) -> u64 {
const MAX: u64 = 2_u64.pow($bits); const HALF: u64 = MAX / 2; const BITS: usize = $bits;
const VAL_MASK: u64 = ((1 << BITS) - 1);
const ROC_MASK: i64 = (u64::MAX >> BITS) as i64;
let seq = seq as u64 & VAL_MASK;
if prev_ext_seq.is_none() {
return seq;
}
let prev_index = prev_ext_seq.unwrap();
let roc = (prev_index >> BITS) as i64; let prev_seq = prev_index & (MAX - 1);
let v = if prev_seq < HALF {
if seq > HALF + prev_seq {
(roc - 1) & ROC_MASK
} else {
roc
}
} else if prev_seq > seq + HALF {
(roc + 1) & ROC_MASK
} else {
roc
};
if v < 0 {
return 0;
}
(v as u64) * MAX + seq
}
};
}
mk_extend!(extend_u8, u8, 8);
mk_extend!(extend_u16, u16, 16);
mk_extend!(extend_u32, u32, 32);
mk_extend!(extend_u7, u8, 7);
mk_extend!(extend_u15, u16, 15);
impl Default for RtpHeader {
fn default() -> Self {
Self {
version: 2,
has_padding: false,
has_extension: true,
csrc_count: 0,
marker: false,
payload_type: 1.into(),
sequence_number: 0,
timestamp: 0,
ssrc: 0.into(),
csrc: [0; 15],
ext_vals: ExtensionValues::default(),
header_len: 16,
}
}
}
#[cfg(test)]
mod test {
use crate::util::already_happened;
use crate::{io::DATAGRAM_MAX_PACKET_SIZE, rtp_::Extension};
use std::time::Duration;
use super::*;
#[test]
fn extend_u16_wrap_around() {
assert_eq!(extend_u16(None, 0), 0);
assert_eq!(extend_u16(Some(0), 1), 1);
assert_eq!(extend_u16(Some(65_535), 0), 65_536);
assert_eq!(extend_u16(Some(65_500), 2), 65_538);
assert_eq!(extend_u16(Some(2), 1), 1);
assert_eq!(extend_u16(Some(65_538), 1), 65_537);
assert_eq!(extend_u16(Some(3), 3), 3);
assert_eq!(extend_u16(Some(65_500), 65_500), 65_500);
}
#[test]
fn extend_u16_with_0_prev() {
let seq = u16::MAX / 2 + 2;
let expected = u64::MAX - (u16::MAX - seq) as u64;
assert_eq!(extend_u16(Some(0), seq), expected);
}
#[test]
fn extend_u32_wrap_around() {
const U32MAX: u64 = u32::MAX as u64 + 1;
assert_eq!(extend_u32(None, 0), 0);
assert_eq!(extend_u32(Some(0), 1), 1);
assert_eq!(extend_u32(Some(U32MAX - 1), 0), U32MAX);
assert_eq!(extend_u32(Some(U32MAX - 32), 2), U32MAX + 2);
assert_eq!(extend_u32(Some(2), 1), 1);
assert_eq!(extend_u32(Some(U32MAX + 2), 1), U32MAX + 1);
assert_eq!(extend_u32(Some(3), 3), 3);
assert_eq!(
extend_u32(Some(U32MAX - 32), (U32MAX - 32) as u32),
U32MAX - 32
);
}
#[test]
fn extend_u32_with_0_prev() {
let seq = u32::MAX / 2 + 2;
let expected = u64::MAX - (u32::MAX - seq) as u64;
assert_eq!(extend_u32(Some(0), seq), expected);
}
#[test]
fn extend_u7_wrap_around() {
let max = 2_u64.pow(7) - 1;
let half = max / 2;
assert_eq!(extend_u7(None, 0), 0);
assert_eq!(extend_u7(None, 1), 1);
assert_eq!(extend_u7(None, max as u8), max);
assert_eq!(extend_u7(None, (max + 1) as u8), 0);
assert_eq!(extend_u7(Some(0), half as u8), half);
assert_eq!(extend_u7(Some(0), (half + 1) as u8), half + 1);
let seq = max / 2 + 2;
let expected = u64::MAX - (max - seq);
assert_eq!(extend_u7(Some(0), seq as u8), expected);
}
#[test]
fn extend_u15_wrap_around() {
let max = 2_u64.pow(15) - 1;
let half = max / 2;
assert_eq!(extend_u15(None, 0), 0);
assert_eq!(extend_u15(None, 1), 1);
assert_eq!(extend_u15(None, max as u16), max);
assert_eq!(extend_u15(None, (max + 1) as u16), 0);
assert_eq!(extend_u15(Some(0), half as u16), half);
assert_eq!(extend_u15(Some(0), (half + 1) as u16), half + 1);
let seq = max / 2 + 2;
let expected = u64::MAX - (max - seq);
assert_eq!(extend_u15(Some(0), seq as u16), expected);
}
#[test]
fn test_generate_one_length_padding_packet() {
let mut buf = vec![6; 255];
RtpHeader::create_padding_packet(&mut buf, 10, 1, 16);
let mut expected = vec![0; 16];
expected[15] = 16;
assert_eq!(&buf[10..26], &expected);
}
#[test]
fn test_write_rtp_headers() {
fn mk_header(seq: u16, ts: u32, level: i8, marker: bool, exts: &ExtensionMap) -> Vec<u8> {
let header = RtpHeader {
payload_type: 33.into(),
sequence_number: seq,
timestamp: ts,
ssrc: 44.into(),
marker,
ext_vals: ExtensionValues {
audio_level: Some(level),
voice_activity: Some(false),
..Default::default()
},
..Default::default()
};
let mut buf = vec![0; DATAGRAM_MAX_PACKET_SIZE];
let n = header.write_to(&mut buf[..], exts);
buf.truncate(n);
buf
}
let mut exts = ExtensionMap::empty();
exts.set(3, Extension::AudioLevel);
let buf1 = mk_header(47_000, 10_000, -42, false, &exts);
let buf2 = mk_header(47_001, 12_000, -43, true, &exts);
let buf3 = mk_header(47_002, 14_000, -44, false, &exts);
let p1 = &[
144, 33, 183, 152, 0, 0, 39, 16, 0, 0, 0, 44, 0xBE, 0xDE, 0, 1, 48, 170, 0, 0,
];
let p2 = &[
144, 161, 183, 153, 0, 0, 46, 224, 0, 0, 0, 44, 0xBE, 0xDE, 0, 1, 48, 171, 0, 0,
];
let p3 = &[
144, 33, 183, 154, 0, 0, 54, 176, 0, 0, 0, 44, 0xBE, 0xDE, 0, 1, 48, 172, 0, 0,
];
assert_eq!(&buf1, p1);
assert_eq!(&buf2, p2);
assert_eq!(&buf3, p3);
}
#[test]
fn test_write_rtp_headers_two_byte_form() {
fn mk_header(seq: u16, ts: u32, level: i8, marker: bool, exts: &ExtensionMap) -> Vec<u8> {
let header = RtpHeader {
payload_type: 33.into(),
sequence_number: seq,
timestamp: ts,
ssrc: 44.into(),
marker,
ext_vals: ExtensionValues {
audio_level: Some(level),
voice_activity: Some(false),
..Default::default()
},
..Default::default()
};
let mut buf = vec![0; DATAGRAM_MAX_PACKET_SIZE];
let n = header.write_to(&mut buf[..], exts);
buf.truncate(n);
buf
}
let mut exts = ExtensionMap::empty();
exts.set(15, Extension::AudioLevel);
let buf1 = mk_header(47_000, 10_000, -42, false, &exts);
let buf2 = mk_header(47_001, 12_000, -43, true, &exts);
let buf3 = mk_header(47_002, 14_000, -44, false, &exts);
let p1 = &[
144, 33, 183, 152, 0, 0, 39, 16, 0, 0, 0, 44, 0x10, 0x00, 0, 1, 15, 1, 170, 0,
];
let p2 = &[
144, 161, 183, 153, 0, 0, 46, 224, 0, 0, 0, 44, 0x10, 0x00, 0, 1, 15, 1, 171, 0,
];
let p3 = &[
144, 33, 183, 154, 0, 0, 54, 176, 0, 0, 0, 44, 0x10, 0x00, 0, 1, 15, 1, 172, 0,
];
assert_eq!(&buf1, p1);
assert_eq!(&buf2, p2);
assert_eq!(&buf3, p3);
}
#[test]
fn test_parse_rtp_headers() {
let exts = ExtensionMap::standard();
let hb1 = [
176, 111, 183, 152, 0, 0, 39, 16, 46, 87, 21, 249, 0xBE, 0xDE, 0, 4, 16, 170, 34, 254,
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,
0, 12,
];
let hb2 = [
176, 111, 183, 153, 0, 0, 46, 224, 46, 87, 21, 249, 0xBE, 0xDE, 0, 4, 16, 171, 34, 254,
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,
0, 0, 12,
];
let hb3 = [
176, 111, 183, 154, 0, 0, 54, 176, 46, 87, 21, 249, 0xBE, 0xDE, 0, 4, 16, 172, 34, 254,
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,
0, 0, 12,
];
let h1 = RtpHeader::parse(&hb1, &exts).unwrap();
let abs1 = already_happened() + Duration::from_micros(63531654);
assert_eq!(
h1,
RtpHeader {
version: 2,
has_padding: true,
has_extension: true,
csrc_count: 0,
marker: false,
payload_type: 111.into(),
sequence_number: 47000,
timestamp: 10000,
ssrc: 777459193.into(),
csrc: [0; 15],
ext_vals: ExtensionValues {
mid: Some("xYj".into()),
abs_send_time: Some(abs1),
voice_activity: Some(true),
audio_level: Some(-42),
transport_cc: Some(0),
..Default::default()
},
header_len: 32
}
);
let h2 = RtpHeader::parse(&hb2, &exts).unwrap();
let abs2 = already_happened() + Duration::from_micros(63631652);
assert_eq!(
h2,
RtpHeader {
version: 2,
has_padding: true,
has_extension: true,
csrc_count: 0,
marker: false,
payload_type: 111.into(),
sequence_number: 47001,
timestamp: 12000,
ssrc: 777459193.into(),
csrc: [0; 15],
ext_vals: ExtensionValues {
mid: Some("xYj".into()),
abs_send_time: Some(abs2),
voice_activity: Some(true),
audio_level: Some(-43),
transport_cc: Some(2),
..Default::default()
},
header_len: 32
}
);
let h3 = RtpHeader::parse(&hb3, &exts).unwrap();
let abs3 = already_happened() + Duration::from_micros(63531654);
assert_eq!(
h3,
RtpHeader {
version: 2,
has_padding: true,
has_extension: true,
csrc_count: 0,
marker: false,
payload_type: 111.into(),
sequence_number: 47002,
timestamp: 14000,
ssrc: 777459193.into(),
csrc: [0; 15],
ext_vals: ExtensionValues {
mid: Some("xYj".into()),
abs_send_time: Some(abs3),
voice_activity: Some(true),
audio_level: Some(-44),
transport_cc: Some(1),
..Default::default()
},
header_len: 32
}
);
}
#[test]
fn test_parse_rtp_headers_two_byte_form() {
let exts = ExtensionMap::standard();
let hb1 = [
176, 111, 183, 152, 0, 0, 39, 16, 46, 87, 21, 249, 0x10, 0x01, 0, 5, 1, 1, 170, 2, 3,
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,
0, 0, 0, 0, 12,
];
let hb2 = [
176, 111, 183, 153, 0, 0, 46, 224, 46, 87, 21, 249, 0x10, 0x02, 0, 5, 1, 1, 171, 2, 3,
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,
0, 0, 0, 0, 0, 12,
];
let hb3 = [
176, 111, 183, 154, 0, 0, 54, 176, 46, 87, 21, 249, 0x10, 0x03, 0, 5, 1, 1, 172, 2, 3,
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,
0, 0, 0, 0, 0, 12,
];
let h1 = RtpHeader::parse(&hb1, &exts).unwrap();
let abs1 = already_happened() + Duration::from_micros(63531654);
assert_eq!(
h1,
RtpHeader {
version: 2,
has_padding: true,
has_extension: true,
csrc_count: 0,
marker: false,
payload_type: 111.into(),
sequence_number: 47000,
timestamp: 10000,
ssrc: 777459193.into(),
csrc: [0; 15],
ext_vals: ExtensionValues {
mid: Some("xYj".into()),
abs_send_time: Some(abs1),
voice_activity: Some(true),
audio_level: Some(-42),
transport_cc: Some(0),
..Default::default()
},
header_len: 36
}
);
let h2 = RtpHeader::parse(&hb2, &exts).unwrap();
let abs2 = already_happened() + Duration::from_micros(63631652);
assert_eq!(
h2,
RtpHeader {
version: 2,
has_padding: true,
has_extension: true,
csrc_count: 0,
marker: false,
payload_type: 111.into(),
sequence_number: 47001,
timestamp: 12000,
ssrc: 777459193.into(),
csrc: [0; 15],
ext_vals: ExtensionValues {
mid: Some("xYj".into()),
abs_send_time: Some(abs2),
voice_activity: Some(true),
audio_level: Some(-43),
transport_cc: Some(2),
..Default::default()
},
header_len: 36
}
);
let h3 = RtpHeader::parse(&hb3, &exts).unwrap();
let abs3 = already_happened() + Duration::from_micros(63531654);
assert_eq!(
h3,
RtpHeader {
version: 2,
has_padding: true,
has_extension: true,
csrc_count: 0,
marker: false,
payload_type: 111.into(),
sequence_number: 47002,
timestamp: 14000,
ssrc: 777459193.into(),
csrc: [0; 15],
ext_vals: ExtensionValues {
mid: Some("xYj".into()),
abs_send_time: Some(abs3),
voice_activity: Some(true),
audio_level: Some(-44),
transport_cc: Some(1),
..Default::default()
},
header_len: 36
}
);
}
#[test]
fn test_write_rtp_headers_with_csrc() {
fn mk_header(csrc_count: usize, csrc: [u32; 15], exts: &ExtensionMap) -> Vec<u8> {
let header = RtpHeader {
csrc_count,
payload_type: 33.into(),
sequence_number: 47_000,
timestamp: 10_000,
ssrc: 44.into(),
csrc,
ext_vals: ExtensionValues {
audio_level: Some(-42),
voice_activity: Some(true),
..Default::default()
},
..Default::default()
};
let mut buf = vec![0; DATAGRAM_MAX_PACKET_SIZE];
let n = header.write_to(&mut buf[..], exts);
buf.truncate(n);
buf
}
let mut exts = ExtensionMap::empty();
exts.set(3, Extension::AudioLevel);
let buf1 = mk_header(
3,
[
100,
0x12_34_56_78,
0xFFFF_FFFF,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
],
&exts,
);
let p1 = &[
147, 33, 183, 152, 0, 0, 39, 16, 0, 0, 0, 44, 0, 0, 0, 100, 0x12, 0x34, 0x56, 0x78, 0xFF, 0xFF, 0xFF, 0xFF, 0xBE, 0xDE, 0, 1, 48, 170, 0, 0, ];
assert_eq!(&buf1, p1);
let buf2 = mk_header(
15,
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
&exts,
);
let p2 = &[
159, 33, 183, 152, 0, 0, 39, 16, 0, 0, 0, 44, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0, 4, 0, 0, 0, 5, 0, 0, 0, 6, 0, 0, 0, 7, 0, 0, 0, 8, 0, 0, 0, 9, 0, 0, 0, 10, 0, 0, 0, 11, 0, 0, 0, 12, 0, 0, 0, 13, 0, 0, 0, 14, 0, 0, 0, 15, 0xBE, 0xDE, 0, 1, 48, 170, 0, 0, ];
assert_eq!(&buf2, p2);
}
#[test]
fn test_parse_rtp_headers_with_csrc() {
let mut exts = ExtensionMap::empty();
exts.set(3, Extension::AudioLevel);
let buf1 = [
147, 33, 183, 152, 0, 0, 39, 16, 0, 0, 0, 44, 0, 0, 0, 100, 0x12, 0x34, 0x56, 0x78,
0xFF, 0xFF, 0xFF, 0xFF, 0xBE, 0xDE, 0, 1, 48, 170, 0, 0,
];
let h1 = RtpHeader::parse(&buf1, &exts).unwrap();
assert_eq!(
h1,
RtpHeader {
version: 2,
has_padding: false,
has_extension: true,
csrc_count: 3,
marker: false,
payload_type: 33.into(),
sequence_number: 47_000,
timestamp: 10_000,
ssrc: 44.into(),
csrc: [
100,
0x12_34_56_78,
0xFFFF_FFFF,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0
],
ext_vals: ExtensionValues {
audio_level: Some(-42),
voice_activity: Some(true),
..Default::default()
},
header_len: 32,
}
);
let buf2 = [
159, 33, 183, 152, 0, 0, 39, 16, 0, 0, 0, 44, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0, 4, 0, 0, 0, 5, 0, 0, 0, 6, 0, 0, 0, 7, 0, 0, 0, 8, 0, 0, 0, 9, 0, 0, 0, 10, 0, 0, 0, 11, 0, 0, 0, 12, 0, 0, 0, 13, 0, 0, 0, 14, 0, 0, 0, 15, 0xBE, 0xDE, 0, 1, 48, 170, 0, 0, ];
let h2 = RtpHeader::parse(&buf2, &exts).unwrap();
assert_eq!(
h2,
RtpHeader {
version: 2,
has_padding: false,
has_extension: true,
csrc_count: 15,
marker: false,
payload_type: 33.into(),
sequence_number: 47_000,
timestamp: 10_000,
ssrc: 44.into(),
csrc: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
ext_vals: ExtensionValues {
audio_level: Some(-42),
voice_activity: Some(true),
..Default::default()
},
header_len: 80,
}
);
}
#[test]
fn truncate_off_srtp_padding() {
let truncate = |mut payload| -> Result<Vec<u8>, ()> {
if RtpHeader::unpad_payload(&mut payload) {
Ok(payload)
} else {
Err(())
}
};
assert_eq!(Ok(vec![1, 2, 3, 4, 0]), truncate(vec![1, 2, 3, 4, 0]));
assert_eq!(Ok(vec![1, 2, 3, 4]), truncate(vec![1, 2, 3, 4, 1]));
assert_eq!(Ok(vec![1, 2, 3]), truncate(vec![1, 2, 3, 4, 2]));
assert_eq!(Ok(vec![1, 2]), truncate(vec![1, 2, 3, 4, 3]));
assert_eq!(Ok(vec![1]), truncate(vec![1, 2, 3, 4, 4]));
assert_eq!(Ok(vec![]), truncate(vec![1, 2, 3, 4, 5]));
assert_eq!(Err(()), truncate(vec![1, 2, 3, 4, 6]));
assert_eq!(Err(()), truncate(vec![1, 2, 3, 4, 255]));
assert_eq!(Ok(vec![0]), truncate(vec![0]));
assert_eq!(Ok(vec![]), truncate(vec![1]));
assert_eq!(Ok(vec![]), truncate(vec![]));
}
}