use crate::Seq;
#[derive(Debug, Ord, PartialOrd, Eq, PartialEq)]
pub enum RtpPacketBuildError {
BufferTooSmall,
PayloadTypeInvalid,
ExtensionTooLarge,
ExtensionMissingPadding,
}
macro_rules! const_assert {
($x:expr $(,)?) => {
#[allow(unknown_lints, clippy::eq_op)]
{
const ASSERT: [(); 1] = [()];
let _ = ASSERT[!($x) as usize];
}
};
}
pub struct Pad(PadInner);
impl Pad {
pub const fn none() -> Self {
Pad(PadInner::None)
}
pub const fn round_to(pad: u8) -> Self {
const_assert!(pad >= 2);
Pad(PadInner::RoundTo(pad))
}
}
#[derive(Clone)]
enum PadInner {
None,
RoundTo(u8),
}
impl PadInner {
pub fn adjust_len(&self, initial_len: usize) -> Option<usize> {
match self {
PadInner::None => None,
PadInner::RoundTo(n) => {
let remainder = initial_len % *n as usize;
if remainder == 0 {
None
} else {
Some(*n as usize - remainder)
}
},
}
}
}
#[derive(Clone)]
pub struct RtpPacketBuilder<'a> {
padded: PadInner,
marked: bool,
payload_type: u8,
extension: Option<(u16, &'a [u8])>,
payload: Option<&'a [u8]>,
sequence: Seq,
timestamp: u32,
ssrc: u32,
csrcs: [u32; 15],
csrc_count: u8
}
impl<'a> RtpPacketBuilder<'a> {
pub fn new() -> Self {
RtpPacketBuilder {
padded: PadInner::None,
marked: false,
payload_type: 0xFF,
extension: None,
payload: None,
sequence: Seq::from(0),
timestamp: 0,
ssrc: 0,
csrcs: [0u32; 15],
csrc_count: 0
}
}
pub fn payload_type(mut self, payload_type: u8) -> Self {
self.payload_type = payload_type;
self
}
pub fn padded(mut self, pad: Pad) -> Self {
self.padded = pad.0;
self
}
pub fn marked(mut self, flag: bool) -> Self {
self.marked = flag;
self
}
pub fn add_csrc(mut self, csrc: u32) -> Self {
if self.csrc_count == 15 {
self
} else {
self.csrcs[self.csrc_count as usize] = csrc;
self.csrc_count = self.csrc_count + 1;
self
}
}
pub fn set_csrc(mut self, csrcs: &[u32]) -> Self {
if csrcs.len() > 15 {
self.csrc_count = 15;
} else {
self.csrc_count = csrcs.len() as u8;
}
self.csrcs[0..self.csrc_count as usize].copy_from_slice(csrcs);
self
}
pub fn sequence(mut self, seq: Seq) -> Self {
self.sequence = seq;
self
}
pub fn ssrc(mut self, ssrc: u32) -> Self {
self.ssrc = ssrc;
self
}
pub fn timestamp(mut self, timestamp: u32) -> Self {
self.timestamp = timestamp;
self
}
pub fn extension(mut self, id: u16, payload: &'a [u8]) -> Self {
self.extension = Some((id, payload));
self
}
pub fn payload(mut self, payload: &'a [u8]) -> Self {
self.payload = Some(payload);
self
}
pub fn target_length(&self) -> usize {
let mut length = 12usize;
length += self.csrc_count as usize * 4;
length += if let Some((_, ext)) = self.extension { ext.len() + 4 } else { 0 };
length += if let Some(payload) = self.payload { payload.len() } else { 0 };
if let Some(adj) = self.padded.adjust_len(length) {
length += adj;
}
length
}
pub fn build_into_unchecked(&self, target: &mut [u8]) -> usize {
let first_byte = &mut target[0];
*first_byte = 2 << 6;
if self.extension.is_some() {
*first_byte |= 1 << 4;
}
*first_byte |= self.csrc_count;
target[1] = self.payload_type;
if self.marked {
target[1] |= 0x80;
}
target[ 2] = (self.sequence.0 >> 8) as u8;
target[ 3] = (self.sequence.0 & 0xFF) as u8;
target[ 4] = (self.timestamp >> 24) as u8;
target[ 5] = (self.timestamp >> 16) as u8;
target[ 6] = (self.timestamp >> 8) as u8;
target[ 7] = (self.timestamp ) as u8;
target[ 8] = (self.ssrc >> 24) as u8;
target[ 9] = (self.ssrc >> 16) as u8;
target[10] = (self.ssrc >> 8) as u8;
target[11] = (self.ssrc ) as u8;
let mut write_index = 12usize;
for index in 0..self.csrc_count as usize {
let csrc = self.csrcs[index];
target[write_index + 0] = (csrc >> 24) as u8;
target[write_index + 1] = (csrc >> 16) as u8;
target[write_index + 2] = (csrc >> 8) as u8;
target[write_index + 3] = (csrc ) as u8;
write_index = write_index + 4;
}
if let Some((id, payload)) = self.extension {
target[write_index + 0] = (id >> 8) as u8;
target[write_index + 1] = (id & 0xFF) as u8;
let len = payload.len() / 4;
target[write_index + 2] = (len >> 8) as u8;
target[write_index + 3] = (len & 0xFF) as u8;
write_index = write_index + 4;
target[write_index..(write_index + payload.len())].copy_from_slice(payload);
write_index += payload.len();
}
if let Some(payload) = self.payload {
target[write_index..(write_index + payload.len())].copy_from_slice(payload);
write_index += payload.len();
}
if let Some(padded_bytes) = self.padded.adjust_len(write_index) {
target[0] |= 1 << 5;
write_index += padded_bytes;
target[write_index - 1] = padded_bytes as u8;
}
write_index
}
pub fn build_into(&self, target: &mut [u8]) -> Result<usize, RtpPacketBuildError> {
if target.len() < self.target_length() {
return Err(RtpPacketBuildError::BufferTooSmall);
}
self.validate_content()?;
Ok(self.build_into_unchecked(target))
}
pub fn build(&self) -> Result<Vec<u8>, RtpPacketBuildError> {
self.validate_content()?;
let mut buffer = Vec::<u8>::new();
buffer.resize(self.target_length(), 0);
let length = self.build_into_unchecked(buffer.as_mut_slice());
assert_eq!(length, buffer.len());
Ok(buffer)
}
fn validate_content(&self) -> Result<(), RtpPacketBuildError> {
if (self.payload_type & (!0x7F)) != 0 {
return Err(RtpPacketBuildError::PayloadTypeInvalid);
}
if let Some((_, payload)) = self.extension {
if payload.len() > 0xFFFF {
return Err(RtpPacketBuildError::ExtensionTooLarge);
}
if (payload.len() & 0x3) != 0 {
return Err(RtpPacketBuildError::ExtensionMissingPadding);
}
}
Ok(())
}
}
#[cfg(test)]
mod test {
use crate::{RtpPacketBuilder, Pad};
#[test]
fn test_padded() {
let payload = vec![1u8];
let packet = RtpPacketBuilder::new()
.payload_type(1)
.payload(&payload)
.padded(Pad::round_to(4))
.build().unwrap();
assert_eq!(packet.len() & 0x03, 0);
assert!(crate::reader::RtpReader::new(&packet).unwrap().padding().is_some());
}
#[test]
fn test_padding_not_needed() {
let payload = vec![1u8; 4];
let packet = RtpPacketBuilder::new()
.payload_type(1)
.payload(&payload)
.padded(Pad::round_to(4))
.build().unwrap();
assert_eq!(packet.len(), 12 + payload.len());
assert!(crate::reader::RtpReader::new(&packet).unwrap().padding().is_none());
}
#[test]
fn test_not_padded() {
let payload = vec![1u8];
let packet = RtpPacketBuilder::new()
.payload_type(1)
.payload(&payload)
.build().unwrap();
assert_eq!(packet.len() & 0x03, 1);
}
#[test]
fn test_would_run() {
let extension = vec![1u8, 2, 3, 4];
let builder = RtpPacketBuilder::new()
.payload_type(12)
.extension(1, &extension);
let mut buffer = [0u8; 100];
builder.build_into(&mut buffer).unwrap();
}
}