1use 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#[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), wsf(read_net_type), wsf(read_ipver), wsf(read_addr), )),
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#[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, wsf(read_val), )),
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}