diff --git a/src/app.rs b/src/app.rs index 5f3e0c0..cb7c8fa 100644 --- a/src/app.rs +++ b/src/app.rs @@ -34,18 +34,23 @@ impl<'a> App<'a> { const SUBTYPE_MASK: u8 = Self::MAX_COUNT; pub const NAME_LEN: usize = 4; + /// The (optional) padding used by this [`App`] packet pub fn padding(&self) -> Option { parser::parse_padding(self.data) } + /// The SSRC this [`App`] packet refers to pub fn ssrc(&self) -> u32 { parser::parse_ssrc(self.data) } + /// The `name` for this [`App`] packet. The `name` should be a sequence of 4 ASCII + /// characters. pub fn name(&self) -> [u8; App::NAME_LEN] { self.data[8..8 + Self::NAME_LEN].try_into().unwrap() } + /// The `name` for this [`App`] as a string. pub fn get_name_string(&self) -> Result { // name is fixed length potentially zero terminated String::from_utf8(Vec::from_iter(self.name().iter().map_while(|&b| { @@ -57,6 +62,7 @@ impl<'a> App<'a> { }))) } + /// The application specific data pub fn data(&self) -> &[u8] { &self.data[12..self.data.len() - self.padding().unwrap_or(0) as usize] } @@ -97,11 +103,13 @@ impl<'a> AppBuilder<'a> { self } + /// The subtype to use for this [`App`] packet pub fn subtype(mut self, subtype: u8) -> Self { self.subtype = subtype; self } + /// The data to use for this [`App`] packet pub fn data(mut self, data: &'a [u8]) -> Self { self.data = data; self diff --git a/src/bye.rs b/src/bye.rs index d7326aa..4940679 100644 --- a/src/bye.rs +++ b/src/bye.rs @@ -54,16 +54,19 @@ impl<'a> Bye<'a> { const MAX_SOURCES: u8 = Self::MAX_COUNT; const MAX_REASON_LEN: u8 = 0xff; + /// The (optional) padding used by this [`Bye`] packet pub fn padding(&self) -> Option { parser::parse_padding(self.data) } + /// The list of ssrcs that this [`Bye`] packet refers to pub fn ssrcs(&self) -> impl Iterator + '_ { self.data[4..4 + self.count() as usize * 4] .chunks_exact(4) .map(u32_from_be_bytes) } + /// The (optional) reason for the [`Bye`] as raw data. The reason should be an ASCII string. pub fn reason(&self) -> Option<&[u8]> { let offset = self.count() as usize * 4 + 4; let reason_aligned_len = self @@ -78,10 +81,12 @@ impl<'a> Bye<'a> { Some(&self.data[offset + 1..end]) } + /// The (optional) reason for the [`Bye`] as a string pub fn get_reason_string(&self) -> Option> { self.reason().map(|r| String::from_utf8(r.into())) } + /// Create a new [`ByeBuilder`] pub fn builder() -> ByeBuilder<'a> { ByeBuilder::new() } diff --git a/src/compound.rs b/src/compound.rs index 9951003..58c0730 100644 --- a/src/compound.rs +++ b/src/compound.rs @@ -6,6 +6,8 @@ use crate::{ RtcpPacket, RtcpParseError, RtcpWriteError, }; +/// A (currently) unknown RTCP packet type. Can also be used as a way to parse a custom RTCP packet +/// type. #[derive(Debug, PartialEq, Eq)] pub struct Unknown<'a> { data: &'a [u8], @@ -54,10 +56,13 @@ impl<'a> RtcpPacketParser<'a> for Unknown<'a> { } impl<'a> Unknown<'a> { + /// The data of this RTCP packet pub fn data(&self) -> &[u8] { self.data } + /// Try to parse this unknown RTCP packet as a different RTCP packet. Can be used with an + /// external implementation of [`RtcpPacket`] to parse a custom RTCP packet. pub fn try_as

(&'a self) -> Result where P: RtcpPacket, @@ -66,11 +71,14 @@ impl<'a> Unknown<'a> { TryFrom::try_from(self) } + /// The builder for an [`Unknown`] RTCP packet. The data does not include the 4 byte RTCP + /// header. pub fn builder(type_: u8, data: &'a [u8]) -> UnknownBuilder<'a> { UnknownBuilder::new(type_, data) } } +/// Unknown RTCP packet builder #[derive(Debug)] #[must_use = "The builder must be built to be used"] pub struct UnknownBuilder<'a> { @@ -81,6 +89,8 @@ pub struct UnknownBuilder<'a> { } impl<'a> UnknownBuilder<'a> { + /// Create a new builder for an [`Unknown`] RTCP packet. The data does not include the 4 byte RTCP + /// header. pub fn new(type_: u8, data: &'a [u8]) -> UnknownBuilder<'a> { UnknownBuilder { padding: 0, @@ -90,11 +100,14 @@ impl<'a> UnknownBuilder<'a> { } } + /// Sets the number of padding bytes to use for this Unknown packet pub fn padding(mut self, padding: u8) -> Self { self.padding = padding; self } + /// Set the count (or possibly type) field in the RTCP header. The exact interpretation of + /// this value is RTCP packet specific. pub fn count(mut self, count: u8) -> Self { self.count = count; self @@ -150,6 +163,8 @@ impl<'a> RtcpPacketWriter for UnknownBuilder<'a> { } } +/// A (closed) enum of all currently known RTCP packet types. The Unknown variant can be used to +/// parse a custom RTCP packet. #[derive(Debug)] pub enum Packet<'a> { App(crate::App<'a>), @@ -217,6 +232,7 @@ impl<'a> RtcpPacketParser<'a> for Packet<'a> { } impl<'a> Packet<'a> { + /// Try parsing this [`Packet`] as a particular [`RtcpPacket`] implementation. pub fn try_as

(&'a self) -> Result where P: RtcpPacket, @@ -226,6 +242,7 @@ impl<'a> Packet<'a> { } } +/// A compound RTCP packet consisting of multiple RTCP packets one after the other #[derive(Debug)] pub struct Compound<'a> { data: &'a [u8], @@ -234,6 +251,9 @@ pub struct Compound<'a> { } impl<'a> Compound<'a> { + /// Parse data into a [`Compound`] RTCP packet. + /// + /// This will validate that the length of each individual RTCP packet is valid upfront. pub fn parse(data: &'a [u8]) -> Result { let mut offset = 0; let mut packet_length; @@ -271,6 +291,7 @@ impl<'a> Compound<'a> { }) } + /// Create a new [`CompoundBuilder`] pub fn builder() -> CompoundBuilder<'a> { CompoundBuilder::default() } @@ -300,6 +321,7 @@ impl<'a> Iterator for Compound<'a> { } } +/// A builder for a RTCP packet #[derive(Debug)] #[must_use = "The builder must be built to be used"] pub enum PacketBuilder<'a> { @@ -357,6 +379,7 @@ impl<'a> RtcpPacketWriter for PacketBuilder<'a> { } } +/// A builder for a [`Compound`] RTCP packet #[derive(Default, Debug)] #[must_use = "The builder must be built to be used"] pub struct CompoundBuilder<'a> { @@ -364,6 +387,7 @@ pub struct CompoundBuilder<'a> { } impl<'a> CompoundBuilder<'a> { + /// Add a packet to the compound rtcp packet pub fn add_packet(mut self, packet: impl RtcpPacketWriter + 'a) -> Self { self.packets.push(Box::new(packet)); self diff --git a/src/feedback/fir.rs b/src/feedback/fir.rs index c41c936..9fa6d9b 100644 --- a/src/feedback/fir.rs +++ b/src/feedback/fir.rs @@ -7,6 +7,7 @@ use crate::prelude::*; use crate::utils::u32_from_be_bytes; use crate::{RtcpParseError, RtcpWriteError}; +/// An entry in a Full Intra Refresh #[derive(Debug, PartialEq, Eq)] pub struct FirEntry { ssrc: u32, @@ -18,10 +19,12 @@ impl FirEntry { Self { ssrc, sequence } } + /// The SSRC for this FIR pub fn ssrc(&self) -> u32 { self.ssrc } + /// The sequence count of the request. Intended for deduplication. pub fn sequence(&self) -> u8 { self.sequence } @@ -63,6 +66,7 @@ impl<'a> Fir<'a> { FirParserEntryIter { parser: self, i: 0 } } + /// Create a new [`FirBuilder`] pub fn builder() -> FirBuilder { FirBuilder::default() } @@ -83,12 +87,14 @@ impl<'a> FciParser<'a> for Fir<'a> { } } +/// Builder for a Full Intra Refresh packet #[derive(Debug, Default)] pub struct FirBuilder { ssrc_seq: HashMap, } impl FirBuilder { + /// Add an SSRC to this FIR packet. An existing SSRC will have their sequence number updated. pub fn add_ssrc(mut self, ssrc: u32, sequence: u8) -> Self { self.ssrc_seq .entry(ssrc) diff --git a/src/feedback/mod.rs b/src/feedback/mod.rs index fe4c375..4d3fbe9 100644 --- a/src/feedback/mod.rs +++ b/src/feedback/mod.rs @@ -14,6 +14,8 @@ pub mod pli; pub mod rpsi; pub mod sli; +/// The type of feedback packet. There is currently transport and payload values supported for +/// feedback packets. #[derive(Debug, Default, PartialEq, Eq)] pub struct FciFeedbackPacketType { transport: bool, @@ -109,18 +111,27 @@ impl<'a> TransportFeedback<'a> { } } + /// The (optional) padding used by this [`TransportFeedback`] packet pub fn padding(&self) -> Option { parser::parse_padding(self.data) } + /// The SSRC of the sender sending this feedback pub fn sender_ssrc(&self) -> u32 { parser::parse_ssrc(self.data) } + /// The SSRC of the media this packet refers to. May be unset (0) in some feedback types. pub fn media_ssrc(&self) -> u32 { parser::parse_ssrc(&self.data[4..]) } + /// Parse the Feedback Control Information into a concrete type. + /// + /// Will fail if: + /// * The FCI does not support transport feedback, + /// * the feedback type does not match the FCI + /// * The FCI implementation fails to parse the contained data pub fn parse_fci>(&self) -> Result { if F::PACKET_TYPE & FciFeedbackPacketType::TRANSPORT == FciFeedbackPacketType::NONE { return Err(RtcpParseError::WrongImplementation); @@ -143,11 +154,14 @@ pub struct TransportFeedbackBuilder<'a> { } impl<'a> TransportFeedbackBuilder<'a> { + /// Set the SSRC this feedback packet is being sent from pub fn sender_ssrc(mut self, sender_ssrc: u32) -> Self { self.sender_ssrc = sender_ssrc; self } + /// Set the SSRC this feedback packet is being directed towards. May be 0 if the specific + /// feedback type allows. pub fn media_ssrc(mut self, media_ssrc: u32) -> Self { self.media_ssrc = media_ssrc; self @@ -293,18 +307,27 @@ impl<'a> PayloadFeedback<'a> { } } + /// The (optional) padding used by this [`PayloadFeedback`] packet pub fn padding(&self) -> Option { parser::parse_padding(self.data) } + /// The SSRC of the sender sending this feedback pub fn sender_ssrc(&self) -> u32 { parser::parse_ssrc(self.data) } + /// The SSRC of the media this packet refers to. May be unset (0) in some feedback types. pub fn media_ssrc(&self) -> u32 { parser::parse_ssrc(&self.data[4..]) } + /// Parse the Feedback Control Information into a concrete type. + /// + /// Will fail if: + /// * The FCI does not support payload feedback, + /// * the feedback type does not match the FCI + /// * The FCI implementation fails to parse the contained data pub fn parse_fci>(&self) -> Result { if F::PACKET_TYPE & FciFeedbackPacketType::PAYLOAD == FciFeedbackPacketType::NONE { return Err(RtcpParseError::WrongImplementation); @@ -327,11 +350,14 @@ pub struct PayloadFeedbackBuilder<'a> { } impl<'a> PayloadFeedbackBuilder<'a> { + /// Set the SSRC this feedback packet is being sent from pub fn sender_ssrc(mut self, sender_ssrc: u32) -> Self { self.sender_ssrc = sender_ssrc; self } + /// Set the SSRC this feedback packet is being directed towards. May be 0 if the specific + /// feedback type allows. pub fn media_ssrc(mut self, media_ssrc: u32) -> Self { self.media_ssrc = media_ssrc; self @@ -392,6 +418,7 @@ impl<'a> RtcpPacketWriter for PayloadFeedbackBuilder<'a> { } } +/// Trait for parsing FCI data in [`TransportFeedback`] or [`PayloadFeedback`] packets pub trait FciParser<'a>: Sized { const PACKET_TYPE: FciFeedbackPacketType; const FCI_FORMAT: u8; @@ -446,8 +473,11 @@ impl<'a> std::ops::Deref for FciBuilderWrapper<'a> { } } +/// Trait for writing a particular FCI implementation with a [`TransportFeedbackBuilder`] or +/// [`PayloadFeedbackBuilder`]. pub trait FciBuilder<'a>: RtcpPacketWriter { /// The format field value to place in the RTCP header fn format(&self) -> u8; + /// The type of feedback packet this FCI data supports being placed in fn supports_feedback_type(&self) -> FciFeedbackPacketType; } diff --git a/src/feedback/nack.rs b/src/feedback/nack.rs index e0337c8..613fb03 100644 --- a/src/feedback/nack.rs +++ b/src/feedback/nack.rs @@ -6,6 +6,7 @@ use crate::feedback::FciFeedbackPacketType; use crate::{prelude::*, utils::u16_from_be_bytes}; use crate::{RtcpParseError, RtcpWriteError}; +/// An iterator over a parsed list of Negative Acknowledgements pub struct NackParserEntryIter<'a> { parser: &'a Nack<'a>, i: usize, @@ -71,6 +72,7 @@ impl<'a> Nack<'a> { } } + /// Create a new [`NackBuilder`] pub fn builder() -> NackBuilder { NackBuilder::default() } @@ -85,7 +87,7 @@ impl<'a> FciParser<'a> for Nack<'a> { } } -pub struct NackBuilderEntryIter> { +struct NackBuilderEntryIter> { i: usize, base_entry: Option, seq_iter: I, @@ -138,12 +140,14 @@ impl> std::iter::Iterator for NackBuilderEntryIter { } } +/// A builder for [`Nack`] #[derive(Debug, Default)] pub struct NackBuilder { rtp_seq: BTreeSet, } impl NackBuilder { + /// Add a RTP sequence number as negatively acknowledged pub fn add_rtp_sequence(mut self, rtp_sequence: u16) -> Self { self.rtp_seq.insert(rtp_sequence); self diff --git a/src/feedback/pli.rs b/src/feedback/pli.rs index 4b33a16..975e80c 100644 --- a/src/feedback/pli.rs +++ b/src/feedback/pli.rs @@ -30,6 +30,7 @@ impl<'a> FciParser<'a> for Pli<'a> { } } +/// Builder for Picture Loss Information #[derive(Debug)] pub struct PliBuilder {} diff --git a/src/feedback/rpsi.rs b/src/feedback/rpsi.rs index 08de510..e2396f3 100644 --- a/src/feedback/rpsi.rs +++ b/src/feedback/rpsi.rs @@ -6,20 +6,25 @@ use crate::feedback::FciFeedbackPacketType; use crate::utils::pad_to_4bytes; use crate::{prelude::*, RtcpParseError, RtcpWriteError}; +/// Reference Picture Selection Indication information #[derive(Debug)] pub struct Rpsi<'a> { data: &'a [u8], } impl<'a> Rpsi<'a> { + /// Create a new [`RpsiBuilder`] pub fn builder() -> RpsiBuilder<'a> { RpsiBuilder::default() } + /// The payload type this RPSI references pub fn payload_type(&self) -> u8 { self.data[1] & 0x7f } + /// The codec specific bit string, that this RPSI contains. + /// Returns that bit string data and how many bits to remove from the last byte pub fn bit_string(&self) -> (&[u8], usize) { let padding_bytes = self.padding_bytes(); let padding_bits = self.data[0] as usize - padding_bytes * 8; @@ -54,6 +59,7 @@ impl<'a> FciParser<'a> for Rpsi<'a> { } } +/// Reference Picture Selection Indication builder #[derive(Debug, Default)] pub struct RpsiBuilder<'a> { payload_type: u8, @@ -62,11 +68,14 @@ pub struct RpsiBuilder<'a> { } impl<'a> RpsiBuilder<'a> { + /// Set the payload type that this RPSI should reference pub fn payload_type(mut self, payload_type: u8) -> Self { self.payload_type = payload_type; self } + /// Set the codec specific bit string for thie RPSI along with how many bits in the last byte + /// must be ignored. pub fn native_data(mut self, data: impl Into>, bit_overrun: u8) -> Self { self.native_bit_string = data.into(); self.native_bit_overrun = bit_overrun; diff --git a/src/feedback/sli.rs b/src/feedback/sli.rs index 0ea04f5..ab9699f 100644 --- a/src/feedback/sli.rs +++ b/src/feedback/sli.rs @@ -3,12 +3,14 @@ use crate::feedback::FciFeedbackPacketType; use crate::{prelude::*, RtcpParseError, RtcpWriteError}; +/// Slice Loss Information #[derive(Debug)] pub struct Sli<'a> { data: &'a [u8], } impl<'a> Sli<'a> { + /// The macro blocks that have been lost pub fn lost_macroblocks(&self) -> impl Iterator + '_ { MacroBlockIter { data: self.data, @@ -16,6 +18,7 @@ impl<'a> Sli<'a> { } } + /// Create a new [`SliBuilder`] pub fn builder() -> SliBuilder { SliBuilder { lost_mbs: vec![] } } @@ -59,6 +62,7 @@ impl<'a> Iterator for MacroBlockIter<'a> { } } +/// A macro block entry #[derive(Debug, PartialEq, Eq, Copy, Clone)] pub struct MacroBlockEntry { start: u16, @@ -89,12 +93,14 @@ impl MacroBlockEntry { } } +/// Builder for Slice Loss Information #[derive(Debug)] pub struct SliBuilder { lost_mbs: Vec, } impl SliBuilder { + /// Add a lost macro block to the SLI pub fn add_lost_macroblock( mut self, start_macroblock: u16, diff --git a/src/receiver.rs b/src/receiver.rs index e683f43..7f0249e 100644 --- a/src/receiver.rs +++ b/src/receiver.rs @@ -42,24 +42,29 @@ impl<'a> RtcpPacketParser<'a> for ReceiverReport<'a> { impl<'a> ReceiverReport<'a> { const MAX_REPORTS: u8 = Self::MAX_COUNT; + /// The (optional) padding used by this [`ReceiverReport`] packet pub fn padding(&self) -> Option { parser::parse_padding(self.data) } + /// The number of reports contained in this packet pub fn n_reports(&self) -> u8 { self.count() } + /// The SSRC of the entity providing the report pub fn ssrc(&self) -> u32 { parser::parse_ssrc(self.data) } + /// Iterator of report blocks in this [`ReceiverReport`] pub fn report_blocks(&self) -> impl Iterator> + '_ { self.data[Self::MIN_PACKET_LEN..Self::MIN_PACKET_LEN + (self.n_reports() as usize * 24)] .chunks_exact(24) .map(|b| ReportBlock::parse(b).unwrap()) } + /// Create a new [`ReportBlockBuilder`] pub fn builder(ssrc: u32) -> ReceiverReportBuilder { ReceiverReportBuilder::new(ssrc) } diff --git a/src/report_block.rs b/src/report_block.rs index c04f882..0cc00db 100644 --- a/src/report_block.rs +++ b/src/report_block.rs @@ -2,6 +2,8 @@ use crate::{utils::u32_from_be_bytes, RtcpParseError, RtcpWriteError}; +/// A report block as found in a [`SenderReport`](crate::SenderReport) or a +/// [`ReceiverReport`](crate::ReceiverReport) for a received SSRC #[derive(Debug, PartialEq, Eq)] pub struct ReportBlock<'a> { data: &'a [u8; ReportBlock::EXPECTED_SIZE], @@ -10,6 +12,7 @@ pub struct ReportBlock<'a> { impl<'a> ReportBlock<'a> { pub const EXPECTED_SIZE: usize = 24; + /// Parse data into a [`ReportBlock`]. pub fn parse(data: &'a [u8]) -> Result { if data.len() < Self::EXPECTED_SIZE { return Err(RtcpParseError::Truncated { @@ -28,34 +31,42 @@ impl<'a> ReportBlock<'a> { }) } + /// The SSRC that this report describes pub fn ssrc(&self) -> u32 { u32_from_be_bytes(&self.data[0..4]) } + /// The fractional part (out of 256) of packets that have been lost pub fn fraction_lost(&self) -> u8 { self.data[4] } + /// Total count of packets that have been lost. This is a 24-bit value. pub fn cumulative_lost(&self) -> u32 { u32_from_be_bytes(&self.data[4..8]) & 0xffffff } + /// Extended sequence number pub fn extended_sequence_number(&self) -> u32 { u32_from_be_bytes(&self.data[8..12]) } + /// The interarrival jitter of this receiver pub fn interarrival_jitter(&self) -> u32 { u32_from_be_bytes(&self.data[12..16]) } + /// The NTP 16.16 fixed point time that a sender report was last received pub fn last_sender_report_timestamp(&self) -> u32 { u32_from_be_bytes(&self.data[16..20]) } + /// 16.16 fixed point duration since the last sender report was received pub fn delay_since_last_sender_report_timestamp(&self) -> u32 { u32_from_be_bytes(&self.data[20..24]) } + /// Create a new [`ReportBlockBuilder`] pub fn builder(ssrc: u32) -> ReportBlockBuilder { ReportBlockBuilder::new(ssrc) } @@ -87,31 +98,37 @@ impl ReportBlockBuilder { } } + /// The fraction (out of 256) of packets lost pub fn fraction_lost(mut self, fraction_lost: u8) -> Self { self.fraction_lost = fraction_lost; self } + /// The cumulative count of packets lost. Value must be limited to the sie of a 24-bit value. pub fn cumulative_lost(mut self, cumulative_lost: u32) -> Self { self.cumulative_lost = cumulative_lost; self } + /// The extended sequence number pub fn extended_sequence_number(mut self, extended_sequence_number: u32) -> Self { self.extended_sequence_number = extended_sequence_number; self } + /// The inter arrival jitter pub fn interarrival_jitter(mut self, interarrival_jitter: u32) -> Self { self.interarrival_jitter = interarrival_jitter; self } + /// The NTP 16.16 fixed point time of the last sender report pub fn last_sender_report_timestamp(mut self, last_sender_report_timestamp: u32) -> Self { self.last_sender_report_timestamp = last_sender_report_timestamp; self } + /// The NTP 16.16 fixed point duration since the last sender report pub fn delay_since_last_sender_report_timestamp( mut self, delay_since_last_sender_report_timestamp: u32, diff --git a/src/sdes.rs b/src/sdes.rs index bd7cead..4e826fc 100644 --- a/src/sdes.rs +++ b/src/sdes.rs @@ -46,19 +46,23 @@ impl<'a> RtcpPacketParser<'a> for Sdes<'a> { } impl<'a> Sdes<'a> { + /// The (optional) padding used by this [`Sdes`] packet pub fn padding(&self) -> Option { parser::parse_padding(self.data) } + /// The chunks contained in this SDES pub fn chunks(&'a self) -> impl Iterator> { self.chunks.iter() } + /// Create a new [`SdesBuilder`] pub fn builder() -> SdesBuilder<'a> { SdesBuilder::default() } } +/// A SDES chunk containing a single SSRC with possibly multiple SDES items #[derive(Clone, Debug, PartialEq, Eq)] pub struct SdesChunk<'a> { ssrc: u32, @@ -113,24 +117,29 @@ impl<'a> SdesChunk<'a> { Ok((ret, offset)) } + /// The SSRC that this chunk describes pub fn ssrc(&self) -> u32 { self.ssrc } + /// The length of this chunk pub fn length(&self) -> usize { let len = Self::MIN_LEN + self.items.iter().fold(0, |acc, item| acc + item.length()); pad_to_4bytes(len) } + /// The items in this chunk pub fn items(&'a self) -> impl Iterator> { self.items.iter() } + /// Create a new [`SdesItemBuilder`] pub fn builder(ssrc: u32) -> SdesChunkBuilder<'a> { SdesChunkBuilder::new(ssrc) } } +/// An SDES item #[derive(Clone, Debug, Eq, PartialEq)] pub struct SdesItem<'a> { data: &'a [u8], @@ -195,14 +204,17 @@ impl<'a> SdesItem<'a> { Ok((item, end)) } + /// The type of this item pub fn type_(&self) -> u8 { self.data[0] } + /// The length of this item pub fn length(&self) -> usize { self.data[1] as usize } + /// The value of this item pub fn value(&self) -> &[u8] { if self.type_() == Self::PRIV { let offset = self.priv_value_offset() as usize; @@ -212,6 +224,7 @@ impl<'a> SdesItem<'a> { } } + /// The value of this item as a string pub fn get_value_string(&self) -> Result { String::from_utf8(self.value().into()) } @@ -247,6 +260,7 @@ impl<'a> SdesItem<'a> { &self.data[3..3 + self.priv_prefix_len() as usize] } + /// Create a new [`SdesItemBuilder`] pub fn builder(type_: u8, value: &'a str) -> SdesItemBuilder<'a> { SdesItemBuilder::new(type_, value) } @@ -347,6 +361,7 @@ impl<'a> SdesChunkBuilder<'a> { } } + /// Add an item to this chunk pub fn add_item(mut self, item: SdesItemBuilder<'a>) -> Self { self.items.push(item); self @@ -416,6 +431,7 @@ impl<'a> SdesChunkBuilder<'a> { } } +/// SDES item builder #[derive(Debug)] #[must_use = "The builder must be built to be used"] pub struct SdesItemBuilder<'a> { diff --git a/src/sender.rs b/src/sender.rs index 1f9f042..9761e6d 100644 --- a/src/sender.rs +++ b/src/sender.rs @@ -42,40 +42,49 @@ impl<'a> RtcpPacketParser<'a> for SenderReport<'a> { impl<'a> SenderReport<'a> { const MAX_REPORTS: u8 = Self::MAX_COUNT; + /// The (optional) padding used by this [`SenderReport`] packet pub fn padding(&self) -> Option { parser::parse_padding(self.data) } + /// The number of reports contained in this packet pub fn n_reports(&self) -> u8 { self.count() } + /// The SSRC of the entity providing the report pub fn ssrc(&self) -> u32 { parser::parse_ssrc(self.data) } + /// The 32.32 fixed point NTP timestamp sampled at the same time as the RTP timestamp pub fn ntp_timestamp(&self) -> u64 { u64_from_be_bytes(&self.data[8..16]) } + /// The RTP timestamp for this SSRC sampled at the same time as the NTP timestamp pub fn rtp_timestamp(&self) -> u32 { u32_from_be_bytes(&self.data[16..20]) } + /// The number of packets that this sender has sent pub fn packet_count(&self) -> u32 { u32_from_be_bytes(&self.data[20..24]) } + /// The number of octets (bytes) that this sender has sent pub fn octet_count(&self) -> u32 { u32_from_be_bytes(&self.data[24..28]) } + /// Iterator of report blocks in this [`SenderReport`] pub fn report_blocks(&self) -> impl Iterator> + '_ { self.data[Self::MIN_PACKET_LEN..Self::MIN_PACKET_LEN + (self.n_reports() as usize * 24)] .chunks_exact(24) .map(|b| ReportBlock::parse(b).unwrap()) } + /// Create a new [`SenderReportBuilder`] pub fn builder(ssrc: u32) -> SenderReportBuilder { SenderReportBuilder::new(ssrc) } diff --git a/src/utils.rs b/src/utils.rs index 2182ce6..8ce5a12 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -23,6 +23,7 @@ pub(crate) const fn pad_to_4bytes(num: usize) -> usize { (num + 3) & !3 } +/// Parsing utility helpers pub mod parser { use crate::{RtcpPacket, RtcpParseError}; @@ -127,6 +128,7 @@ pub mod parser { } } +/// Writing utility helpers pub mod writer { use crate::{RtcpPacket, RtcpWriteError};