rtp_parse/rtcp/
rtcp_fb_fir.rs

1use crate::{PacketBuffer, PacketBufferMut};
2use anyhow::{bail, Context, Result};
3use bit_cursor::{
4    bit_read_exts::BitReadExts,
5    bit_write_exts::BitWriteExts,
6    byte_order::NetworkOrder,
7    nsw_types::{u24, u5},
8};
9
10use super::{
11    rtcp_fb_header::{write_rtcp_fb_header, RtcpFbHeader},
12    rtcp_header::{write_rtcp_header, RtcpHeader},
13};
14
15/// FIR FCI:
16///
17///  0                   1                   2                   3
18///  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
19/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
20/// |                              SSRC                             |
21/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
22/// | Seq nr.       |    Reserved                                   |
23/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
24///
25/// From https://datatracker.ietf.org/doc/html/rfc5104#section-4.3.1.2:
26/// Within the common packet header for feedback messages (as defined in
27/// section 6.1 of [RFC4585]), the "SSRC of packet sender" field
28/// indicates the source of the request, and the "SSRC of media source"
29/// is not used and SHALL be set to 0.  The SSRCs of the media senders to
30/// which the FIR command applies are in the corresponding FCI entries.
31/// A FIR message MAY contain requests to multiple media senders, using
32/// one FCI entry per target media sender.
33#[derive(Debug)]
34pub struct RtcpFbFirPacket {
35    pub header: RtcpHeader,
36    pub fb_header: RtcpFbHeader,
37    pub fcis: Vec<RtcpFbFirFci>,
38}
39
40impl RtcpFbFirPacket {
41    pub const FMT: u5 = u5::new(4);
42}
43
44pub fn read_rtcp_fb_fir<B: PacketBuffer>(
45    buf: &mut B,
46    header: RtcpHeader,
47    fb_header: RtcpFbHeader,
48) -> Result<RtcpFbFirPacket> {
49    // TODO: there can be multiple FCI chunks here, we need to keep reading until reaching the end
50    // of the packet.  That means the buf we're given needs to be a slice based on the length in
51    // the header so this can read until the end
52    if fb_header.media_source_ssrc != 0 {
53        bail!("SSRC of media source must be set to 0");
54    }
55    let mut num_fci = 1;
56    let mut fcis = Vec::new();
57    while buf.bytes_remaining() >= RtcpFbFirFci::SIZE_BYTES {
58        let fci = read_rtcp_fb_fir_fci(buf).with_context(|| format!("fci {num_fci}"))?;
59        fcis.push(fci);
60        num_fci += 1;
61    }
62    Ok(RtcpFbFirPacket {
63        header,
64        fb_header,
65        fcis,
66    })
67}
68
69pub fn write_rtcp_fb_fir<B: PacketBufferMut>(buf: &mut B, fb_fir: &RtcpFbFirPacket) -> Result<()> {
70    write_rtcp_header(buf, &fb_fir.header).context("header")?;
71    write_rtcp_fb_header(buf, &fb_fir.fb_header).context("fb header")?;
72    for (i, fci) in fb_fir.fcis.iter().enumerate() {
73        write_rtcp_fb_fir_fci(buf, fci).with_context(|| format!("fci {i}"))?;
74    }
75
76    Ok(())
77}
78
79#[derive(Debug)]
80pub struct RtcpFbFirFci {
81    ssrc: u32,
82    seq_num: u8,
83}
84
85impl RtcpFbFirFci {
86    pub const SIZE_BYTES: usize = 8;
87}
88
89pub fn read_rtcp_fb_fir_fci<B: PacketBuffer>(buf: &mut B) -> Result<RtcpFbFirFci> {
90    let ssrc = buf.read_u32::<NetworkOrder>().context("source")?;
91    let seq_num = buf.read_u8().context("seq num")?;
92    // Consume the reserved chunk
93    let _ = buf.read_u24::<NetworkOrder>().context("reserved")?;
94
95    Ok(RtcpFbFirFci { ssrc, seq_num })
96}
97
98pub fn write_rtcp_fb_fir_fci<B: PacketBufferMut>(
99    buf: &mut B,
100    fb_fir_fci: &RtcpFbFirFci,
101) -> Result<()> {
102    buf.write_u32::<NetworkOrder>(fb_fir_fci.ssrc)
103        .context("source")?;
104    buf.write_u8(fb_fir_fci.seq_num).context("seq num")?;
105    buf.write_u24::<NetworkOrder>(u24::new(0))
106        .context("reserved")?;
107
108    Ok(())
109}