sdp_nom/attributes/
rtcp.rs

1//! Rtcp<https://tools.ietf.org/html/rfc3605>
2// ///////////////////////
3use derive_into_owned::IntoOwned;
4use nom::{
5    branch::alt,
6    bytes::complete::tag,
7    combinator::{map, opt},
8    sequence::{preceded, tuple},
9    IResult,
10};
11
12use std::{borrow::Cow, net::IpAddr};
13
14use crate::parsers::*;
15#[cfg(test)]
16use crate::{assert_line, assert_line_print};
17
18#[derive(Clone, PartialEq, Eq)]
19#[cfg_attr(feature = "debug", derive(Debug))]
20#[cfg_attr(
21    feature = "serde",
22    derive(serde::Serialize, serde::Deserialize),
23    serde(rename_all = "camelCase")
24)]
25#[non_exhaustive]
26pub enum NetType {
27    IN,
28}
29
30pub fn read_net_type(input: &str) -> IResult<&str, NetType> {
31    map(tag("IN"), |_| NetType::IN)(input)
32}
33
34/// Rtcp
35///
36///<https://tools.ietf.org/html/rfc3605>
37/// `a=rtcp:65179 IN IP4 10.23.34.567`
38#[derive(Clone, PartialEq, Eq)]
39#[cfg_attr(feature = "debug", derive(Debug))]
40#[cfg_attr(
41    feature = "serde",
42    derive(serde::Serialize, serde::Deserialize),
43    serde(rename_all = "camelCase")
44)]
45pub struct Rtcp {
46    pub port: u32,
47    pub net_type: NetType,
48    pub ip_ver: IpVer,
49    pub addr: IpAddr,
50}
51
52pub fn rtcp_attribute_line(input: &str) -> IResult<&str, Rtcp> {
53    attribute("rtcp", rtcp_attribute)(input)
54}
55
56fn rtcp_attribute(input: &str) -> IResult<&str, Rtcp> {
57    map(
58        tuple((
59            wsf(read_number),   // port
60            wsf(read_net_type), // net_type
61            wsf(read_ipver),    // ip_ver
62            wsf(read_addr),     // addr
63        )),
64        |(port, net_type, ip_ver, addr)| Rtcp {
65            port,
66            net_type,
67            ip_ver,
68            addr,
69        },
70    )(input)
71}
72
73#[test]
74fn test_rtcp_attribute_line() {
75    assert_line_print!(rtcp_attribute_line, "a=rtcp:65179 IN IP4 10.23.34.255");
76    assert_line_print!(rtcp_attribute_line, "a=rtcp:65179 IN IP4 ::1");
77}
78
79// ///////////////////////
80
81/// RtcpFeedback
82///
83/// This one is fun to parse
84///<https://tools.ietf.org/html/rfc6642>
85///<https://tools.ietf.org/html/rfc4585#section-4.2>
86///<https://datatracker.ietf.org/doc/draft-ietf-mmusic-sdp-mux-attributes/16/?include_text=1>
87/// eg `a=rtcp-fb:98 trr-int 100`
88#[derive(Clone, IntoOwned, PartialEq, Eq)]
89#[cfg_attr(feature = "debug", derive(Debug))]
90#[cfg_attr(
91    feature = "serde",
92    derive(serde::Serialize, serde::Deserialize),
93    serde(rename_all = "camelCase")
94)]
95pub struct Fb<'a> {
96    pub payload: u32,
97    pub val: FbVal<'a>,
98}
99
100#[derive(Clone, IntoOwned, PartialEq, Eq)]
101#[cfg_attr(feature = "debug", derive(Debug))]
102#[cfg_attr(
103    feature = "serde",
104    derive(serde::Serialize, serde::Deserialize),
105    serde(rename_all = "camelCase")
106)]
107#[non_exhaustive]
108pub enum FbVal<'a> {
109    Ack(FbAckParam<'a>),
110    Nack(FbNackParam<'a>),
111    TrrInt(u32),
112    RtcpFbId {
113        id: Cow<'a, str>,
114        param: Option<FbParam<'a>>,
115    },
116}
117
118#[derive(Clone, IntoOwned, PartialEq, Eq)]
119#[cfg_attr(feature = "debug", derive(Debug))]
120#[cfg_attr(
121    feature = "serde",
122    derive(serde::Serialize, serde::Deserialize),
123    serde(rename_all = "camelCase")
124)]
125#[non_exhaustive]
126pub enum FbParam<'a> {
127    App(Cow<'a, str>),
128    Single(Cow<'a, str>),
129    Pair(Cow<'a, str>, Cow<'a, str>),
130}
131
132fn read_param(input: &str) -> IResult<&str, FbParam> {
133    alt((
134        map(preceded(tag("app"), wsf(cowify(read_string))), FbParam::App),
135        map(
136            tuple((wsf(cowify(read_string)), wsf(cowify(read_string)))),
137            |(token, value)| FbParam::Pair(token, value),
138        ),
139        map(wsf(cowify(read_string)), FbParam::Single),
140    ))(input)
141}
142
143#[derive(Clone, IntoOwned, PartialEq, Eq)]
144#[cfg_attr(feature = "debug", derive(Debug))]
145#[cfg_attr(
146    feature = "serde",
147    derive(serde::Serialize, serde::Deserialize),
148    serde(rename_all = "camelCase")
149)]
150#[non_exhaustive]
151pub enum FbAckParam<'a> {
152    Rpsi,
153    Sli(Option<Cow<'a, str>>),
154    App(Cow<'a, str>),
155    Other(Cow<'a, str>, Option<Cow<'a, str>>),
156}
157
158fn read_ack_param(input: &str) -> IResult<&str, FbAckParam> {
159    alt((
160        map(tag("rpsi"), |_| FbAckParam::Rpsi),
161        map(
162            preceded(tag("app"), wsf(cowify(read_string))),
163            FbAckParam::App,
164        ),
165        map(
166            preceded(tag("sli"), opt(wsf(cowify(read_string)))),
167            FbAckParam::Sli,
168        ),
169        map(
170            tuple((wsf(cowify(read_string)), wsf(cowify(read_string)))),
171            |(token, value)| FbAckParam::Other(token, Some(value)),
172        ),
173    ))(input)
174}
175
176#[test]
177fn test_rtcpfb_ack_param() {
178    assert_line!(read_ack_param, "sli", FbAckParam::Sli(None));
179    assert_line!(
180        read_ack_param,
181        "sli 5432",
182        FbAckParam::Sli(Some("5432".into()))
183    );
184}
185
186#[derive(Clone, IntoOwned, PartialEq, Eq)]
187#[cfg_attr(feature = "debug", derive(Debug))]
188#[cfg_attr(
189    feature = "serde",
190    derive(serde::Serialize, serde::Deserialize),
191    serde(rename_all = "camelCase")
192)]
193#[non_exhaustive]
194pub enum FbNackParam<'a> {
195    Pli,
196    Sli,
197    Rpsi,
198    App(Cow<'a, str>),
199    Other(Cow<'a, str>, Cow<'a, str>),
200}
201
202fn read_nack_param(input: &str) -> IResult<&str, FbNackParam> {
203    alt((
204        map(tag("rpsi"), |_| FbNackParam::Rpsi),
205        map(
206            preceded(tag("app"), wsf(cowify(read_string))),
207            FbNackParam::App,
208        ),
209        map(
210            tuple((wsf(cowify(read_string)), wsf(cowify(read_string)))),
211            |(token, value)| FbNackParam::Other(token, value),
212        ),
213    ))(input)
214}
215
216fn read_val(input: &str) -> IResult<&str, FbVal> {
217    alt((
218        map(preceded(tag("ack"), wsf(read_ack_param)), FbVal::Ack),
219        map(preceded(tag("nack"), wsf(read_nack_param)), FbVal::Nack),
220        map(preceded(tag("trr-int"), wsf(read_number)), FbVal::TrrInt),
221        map(
222            tuple((wsf(cowify(read_string)), opt(wsf(read_param)))),
223            |(id, param)| FbVal::RtcpFbId { id, param },
224        ),
225    ))(input)
226}
227
228#[test]
229#[rustfmt::skip]
230fn test_read_val() {
231    assert_line!(read_val, "trr-int 100", FbVal::TrrInt(100), print);
232    assert_line!(read_val, "ack sli", FbVal::Ack(FbAckParam::Sli(None)), print);
233    assert_line!(read_val, "ack sli 5432", FbVal::Ack(FbAckParam::Sli(Some("5432".into()))), print);
234    assert_line!(read_val, "nack rpsi", FbVal::Nack(FbNackParam::Rpsi), print);
235    assert_line!(read_val, "goog-remb", FbVal:: RtcpFbId{id: "goog-remb".into(), param: None}, print);
236    assert_line!(read_val, "ccm", FbVal:: RtcpFbId{id: "ccm".into(), param: None}, print);
237    assert_line!(read_val, "ccm fir", FbVal:: RtcpFbId{id: "ccm".into(), param: Some(FbParam::Single("fir".into()))}, print);
238    assert_line!(read_val, "fb foo bar", FbVal:: RtcpFbId{id: "fb".into(), param: Some(FbParam::Pair("foo".into(), "bar".into()))}, print);
239}
240
241pub fn rtcpfb_attribute_line(input: &str) -> IResult<&str, Fb> {
242    attribute("rtcp-fb", rtcpfb_attribute)(input)
243}
244
245fn rtcpfb_attribute(input: &str) -> IResult<&str, Fb> {
246    map(
247        tuple((
248            read_number,   // payload
249            wsf(read_val), // val
250        )),
251        |(payload, val)| Fb { payload, val },
252    )(input)
253}
254
255#[test]
256#[rustfmt::skip]
257fn test_rtcpfb_line() {
258    assert_line_print!(rtcpfb_attribute_line, "a=rtcp-fb:98 trr-int 100");
259    assert_line_print!(rtcpfb_attribute_line, "a=rtcp-fb:98 ack sli");
260    assert_line_print!(rtcpfb_attribute_line, "a=rtcp-fb:98 ack sli 5432");
261    assert_line!(rtcpfb_attribute_line, "a=rtcp-fb:98 nack rpsi", Fb {payload: 98, val: FbVal::Nack(FbNackParam::Rpsi)}, print);
262
263    assert_line!(rtcpfb_attribute_line, "a=rtcp-fb:96 goog-remb", Fb {payload: 96, val: FbVal::RtcpFbId{id: "goog-remb".into(), param: None}}, print);
264    assert_line!(rtcpfb_attribute_line, "a=rtcp-fb:96 transport-cc", Fb {payload: 96, val: FbVal::RtcpFbId{id: "transport-cc".into(), param: None}}, print);
265    assert_line!(
266        rtcpfb_attribute_line,
267        "a=rtcp-fb:96 ccm fir",
268        Fb {
269            payload: 96,
270            val: FbVal::RtcpFbId{
271                id: "ccm".into(),
272                param: Some(FbParam::Single("fir".into()))
273            }
274        }, print
275    );
276}