use std::fmt::Debug;
use anyhow::{anyhow, Result};
use parsely_rs::*;
#[derive(Clone, Debug, PartialEq, Eq, ParselyRead, ParselyWrite)]
#[parsely_write(sync_args("payload_length_bytes: u16", "report_count_value: u5"))]
pub struct RtcpHeader {
#[parsely(assertion = "|v: &u2| *v == 2")]
pub version: u2,
pub has_padding: bool,
#[parsely_write(sync_expr = "report_count_value")]
pub report_count: u5,
pub packet_type: u8,
#[parsely_write(sync_expr = "payload_length_bytes / 4")]
pub length_field: u16,
}
impl Default for RtcpHeader {
fn default() -> Self {
Self {
version: u2::new(2),
has_padding: Default::default(),
report_count: Default::default(),
packet_type: Default::default(),
length_field: Default::default(),
}
}
}
pub fn get_sender_ssrc(buf: &[u8]) -> u32 {
u32::from_be_bytes(buf[4..8].try_into().unwrap())
}
impl RtcpHeader {
pub const SIZE_BYTES: usize = 4;
pub fn payload_length_bytes(&self) -> Result<u16> {
self.length_field
.checked_mul(4)
.ok_or(anyhow!("Invalid length field"))
}
pub fn report_count(mut self, report_count: u5) -> Self {
self.report_count = report_count;
self
}
pub fn packet_type(mut self, packet_type: u8) -> Self {
self.packet_type = packet_type;
self
}
}
#[cfg(test)]
#[allow(clippy::unusual_byte_groupings, clippy::bool_assert_comparison)]
mod tests {
use super::*;
#[test]
fn test_read_rtcp_header() {
let mut buf = Bits::from_static_bytes(&[0b10_0_00001, 202, 0, 42]);
let header = RtcpHeader::read::<NetworkOrder>(&mut buf, ())
.context("rtcp header")
.unwrap();
assert_eq!(header.version, u2::new(2));
assert_eq!(header.has_padding, false);
assert_eq!(header.report_count, u5::new(1));
assert_eq!(header.packet_type, 202);
assert_eq!(header.length_field, 42);
}
#[test]
fn test_write_rtcp_header() {
let header = RtcpHeader {
version: u2::new(2),
has_padding: false,
report_count: u5::new(1),
packet_type: 1,
length_field: 2,
};
let mut buf_mut = BitsMut::new();
header
.write::<NetworkOrder>(&mut buf_mut, ())
.expect("successful write");
let mut buf = buf_mut.freeze();
let read_header = RtcpHeader::read::<NetworkOrder>(&mut buf, ())
.context("rtcp header")
.unwrap();
assert_eq!(header, read_header);
}
}